MediaWiki  master
User.php
Go to the documentation of this file.
1 <?php
30 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
45 use Wikimedia\Assert\Assert;
46 use Wikimedia\Assert\PreconditionException;
47 use Wikimedia\IPUtils;
51 use Wikimedia\ScopedCallback;
52 
69  use ProtectedHookAccessorTrait;
71 
76  public const READ_EXCLUSIVE = IDBAccessObject::READ_EXCLUSIVE;
77 
82  public const READ_LOCKING = IDBAccessObject::READ_LOCKING;
83 
87  public const TOKEN_LENGTH = 32;
88 
92  public const INVALID_TOKEN = '*** INVALID ***';
93 
98  private const VERSION = 17;
99 
105  public const GETOPTIONS_EXCLUDE_DEFAULTS = UserOptionsLookup::EXCLUDE_DEFAULTS;
106 
110  public const CHECK_USER_RIGHTS = true;
111 
115  public const IGNORE_USER_RIGHTS = false;
116 
121  public const MAINTENANCE_SCRIPT_USER = 'Maintenance script';
122 
130  protected static $mCacheVars = [
131  // user table
132  'mId',
133  'mName',
134  'mRealName',
135  'mEmail',
136  'mTouched',
137  'mToken',
138  'mEmailAuthenticated',
139  'mEmailToken',
140  'mEmailTokenExpires',
141  'mRegistration',
142  // actor table
143  'mActorId',
144  ];
145 
147  // Some of these are public, including for use by the UserFactory, but they generally
148  // should not be set manually
149  // @{
151  public $mId;
153  public $mName;
159  public $mActorId;
161  public $mRealName;
162 
164  public $mEmail;
166  public $mTouched;
168  protected $mQuickTouched;
170  protected $mToken;
174  protected $mEmailToken;
178  protected $mRegistration;
179  // @}
180 
181  // @{
185  protected $mLoadedItems = [];
186  // @}
187 
198  public $mFrom;
199 
204  protected $mDatePreference;
211  public $mBlockedby;
213  protected $mHash;
219  protected $mBlockreason;
221  protected $mGlobalBlock;
223  protected $mLocked;
230  public $mHideName;
231 
233  private $mRequest;
234 
240  public $mBlock;
241 
243  protected $mAllowUsertalk;
244 
246  private $mBlockedFromCreateAccount = false;
247 
249  protected $queryFlagsUsed = self::READ_NORMAL;
250 
253 
269  public function __construct() {
270  $this->clearInstanceCache( 'defaults' );
271  }
272 
279  public function getWikiId() {
280  return self::LOCAL;
281  }
282 
286  public function __toString() {
287  return $this->getName();
288  }
289 
290  public function &__get( $name ) {
291  // A shortcut for $mRights deprecation phase
292  if ( $name === 'mRights' ) {
293  $copy = MediaWikiServices::getInstance()
294  ->getPermissionManager()
295  ->getUserPermissions( $this );
296  return $copy;
297  } elseif ( $name === 'mOptions' ) {
298  wfDeprecated( 'User::$mOptions', '1.35' );
299  $options = MediaWikiServices::getInstance()->getUserOptionsLookup()->getOptions( $this );
300  return $options;
301  } elseif ( !property_exists( $this, $name ) ) {
302  // T227688 - do not break $u->foo['bar'] = 1
303  wfLogWarning( 'tried to get non-existent property' );
304  $this->$name = null;
305  return $this->$name;
306  } else {
307  wfLogWarning( 'tried to get non-visible property' );
308  $null = null;
309  return $null;
310  }
311  }
312 
313  public function __set( $name, $value ) {
314  // A shortcut for $mRights deprecation phase, only known legitimate use was for
315  // testing purposes, other uses seem bad in principle
316  if ( $name === 'mRights' ) {
317  MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
318  $this,
319  $value ?? []
320  );
321  } elseif ( $name === 'mOptions' ) {
322  wfDeprecated( 'User::$mOptions', '1.35' );
323  MediaWikiServices::getInstance()->getUserOptionsManager()->clearUserOptionsCache( $this );
324  foreach ( $value as $key => $val ) {
325  $this->setOption( $key, $val );
326  }
327  } elseif ( !property_exists( $this, $name ) ) {
328  $this->$name = $value;
329  } else {
330  wfLogWarning( 'tried to set non-visible property' );
331  }
332  }
333 
334  public function __sleep(): array {
335  return array_diff(
336  array_keys( get_object_vars( $this ) ),
337  [
338  'mThisAsAuthority' // memoization, will be recreated on demand.
339  ]
340  );
341  }
342 
357  public function isSafeToLoad() {
358  global $wgFullyInitialised;
359 
360  // The user is safe to load if:
361  // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data)
362  // * mLoadedItems === true (already loaded)
363  // * mFrom !== 'session' (sessions not involved at all)
364 
365  return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) ||
366  $this->mLoadedItems === true || $this->mFrom !== 'session';
367  }
368 
374  public function load( $flags = self::READ_NORMAL ) {
375  global $wgFullyInitialised;
376 
377  if ( $this->mLoadedItems === true ) {
378  return;
379  }
380 
381  // Set it now to avoid infinite recursion in accessors
382  $oldLoadedItems = $this->mLoadedItems;
383  $this->mLoadedItems = true;
384  $this->queryFlagsUsed = $flags;
385 
386  // If this is called too early, things are likely to break.
387  if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
388  LoggerFactory::getInstance( 'session' )
389  ->warning( 'User::loadFromSession called before the end of Setup.php', [
390  'exception' => new Exception( 'User::loadFromSession called before the end of Setup.php' ),
391  ] );
392  $this->loadDefaults();
393  $this->mLoadedItems = $oldLoadedItems;
394  return;
395  }
396 
397  switch ( $this->mFrom ) {
398  case 'defaults':
399  $this->loadDefaults();
400  break;
401  case 'id':
402  // Make sure this thread sees its own changes, if the ID isn't 0
403  if ( $this->mId != 0 ) {
404  $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
405  if ( $lb->hasOrMadeRecentMasterChanges() ) {
406  $flags |= self::READ_LATEST;
407  $this->queryFlagsUsed = $flags;
408  }
409  }
410 
411  $this->loadFromId( $flags );
412  break;
413  case 'actor':
414  case 'name':
415  // Make sure this thread sees its own changes
416  $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
417  if ( $lb->hasOrMadeRecentMasterChanges() ) {
418  $flags |= self::READ_LATEST;
419  $this->queryFlagsUsed = $flags;
420  }
421 
422  list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
423  $row = wfGetDB( $index )->selectRow(
424  'actor',
425  [ 'actor_id', 'actor_user', 'actor_name' ],
426  $this->mFrom === 'name'
427  // make sure to use normalized form of IP for anonymous users
428  ? [ 'actor_name' => IPUtils::sanitizeIP( $this->mName ) ]
429  : [ 'actor_id' => $this->mActorId ],
430  __METHOD__,
431  $options
432  );
433 
434  if ( !$row ) {
435  // Ugh.
436  $this->loadDefaults( $this->mFrom === 'name' ? $this->mName : false );
437  } elseif ( $row->actor_user ) {
438  $this->mId = $row->actor_user;
439  $this->loadFromId( $flags );
440  } else {
441  $this->loadDefaults( $row->actor_name, $row->actor_id );
442  }
443  break;
444  case 'session':
445  if ( !$this->loadFromSession() ) {
446  // Loading from session failed. Load defaults.
447  $this->loadDefaults();
448  }
449  $this->getHookRunner()->onUserLoadAfterLoadFromSession( $this );
450  break;
451  default:
452  throw new UnexpectedValueException(
453  "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
454  }
455  }
456 
462  public function loadFromId( $flags = self::READ_NORMAL ) {
463  if ( $this->mId == 0 ) {
464  // Anonymous users are not in the database (don't need cache)
465  $this->loadDefaults();
466  return false;
467  }
468 
469  // Try cache (unless this needs data from the primary DB).
470  // NOTE: if this thread called saveSettings(), the cache was cleared.
471  $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
472  if ( $latest ) {
473  if ( !$this->loadFromDatabase( $flags ) ) {
474  // Can't load from ID
475  return false;
476  }
477  } else {
478  $this->loadFromCache();
479  }
480 
481  $this->mLoadedItems = true;
482  $this->queryFlagsUsed = $flags;
483 
484  return true;
485  }
486 
492  public static function purge( $dbDomain, $userId ) {
493  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
494  $key = $cache->makeGlobalKey( 'user', 'id', $dbDomain, $userId );
495  $cache->delete( $key );
496  }
497 
503  protected function getCacheKey( WANObjectCache $cache ) {
504  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
505 
506  return $cache->makeGlobalKey( 'user', 'id', $lbFactory->getLocalDomainID(), $this->mId );
507  }
508 
515  $id = $this->getId();
516 
517  return $id ? [ $this->getCacheKey( $cache ) ] : [];
518  }
519 
526  protected function loadFromCache() {
527  global $wgFullyInitialised;
528 
529  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
530  $data = $cache->getWithSetCallback(
531  $this->getCacheKey( $cache ),
532  $cache::TTL_HOUR,
533  function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache, $wgFullyInitialised ) {
534  $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
535  wfDebug( "User: cache miss for user {$this->mId}" );
536 
537  $this->loadFromDatabase( self::READ_NORMAL );
538 
539  $data = [];
540  foreach ( self::$mCacheVars as $name ) {
541  $data[$name] = $this->$name;
542  }
543 
544  $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
545 
546  if ( $wgFullyInitialised ) {
547  $groupMemberships = MediaWikiServices::getInstance()
548  ->getUserGroupManager()
549  ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
550 
551  // if a user group membership is about to expire, the cache needs to
552  // expire at that time (T163691)
553  foreach ( $groupMemberships as $ugm ) {
554  if ( $ugm->getExpiry() ) {
555  $secondsUntilExpiry =
556  wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
557 
558  if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
559  $ttl = $secondsUntilExpiry;
560  }
561  }
562  }
563  }
564 
565  return $data;
566  },
567  [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ]
568  );
569 
570  // Restore from cache
571  foreach ( self::$mCacheVars as $name ) {
572  $this->$name = $data[$name];
573  }
574 
575  return true;
576  }
577 
578  /***************************************************************************/
579  // region newFrom*() static factory methods
602  public static function newFromName( $name, $validate = 'valid' ) {
603  // Backwards compatibility with strings / false
604  $validationLevels = [
605  'valid' => UserFactory::RIGOR_VALID,
606  'usable' => UserFactory::RIGOR_USABLE,
607  'creatable' => UserFactory::RIGOR_CREATABLE
608  ];
609  if ( $validate === true ) {
610  $validate = 'valid';
611  }
612  if ( $validate === false ) {
613  $validation = UserFactory::RIGOR_NONE;
614  } elseif ( array_key_exists( $validate, $validationLevels ) ) {
615  $validation = $validationLevels[ $validate ];
616  } else {
617  // Not a recognized value, probably a test for unsupported validation
618  // levels, regardless, just pass it along
619  $validation = $validate;
620  }
621 
622  $user = MediaWikiServices::getInstance()
623  ->getUserFactory()
624  ->newFromName( (string)$name, $validation );
625 
626  // UserFactory returns null instead of false
627  if ( $user === null ) {
628  $user = false;
629  }
630  return $user;
631  }
632 
643  public static function newFromId( $id ) {
644  return MediaWikiServices::getInstance()
645  ->getUserFactory()
646  ->newFromId( (int)$id );
647  }
648 
660  public static function newFromActorId( $id ) {
661  return MediaWikiServices::getInstance()
662  ->getUserFactory()
663  ->newFromActorId( (int)$id );
664  }
665 
679  public static function newFromIdentity( UserIdentity $identity ) {
680  // Don't use the service if we already have a User object,
681  // so that User::newFromIdentity calls don't break things in unit tests.
682  if ( $identity instanceof User ) {
683  return $identity;
684  }
685 
686  return MediaWikiServices::getInstance()
687  ->getUserFactory()
688  ->newFromUserIdentity( $identity );
689  }
690 
708  public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain = false ) {
709  return MediaWikiServices::getInstance()
710  ->getUserFactory()
711  ->newFromAnyId( $userId, $userName, $actorId, $dbDomain );
712  }
713 
729  public static function newFromConfirmationCode( $code, $flags = self::READ_NORMAL ) {
730  return MediaWikiServices::getInstance()
731  ->getUserFactory()
732  ->newFromConfirmationCode( (string)$code, $flags );
733  }
734 
742  public static function newFromSession( WebRequest $request = null ) {
743  $user = new User;
744  $user->mFrom = 'session';
745  $user->mRequest = $request;
746  return $user;
747  }
748 
764  public static function newFromRow( $row, $data = null ) {
765  $user = new User;
766  $user->loadFromRow( $row, $data );
767  return $user;
768  }
769 
805  public static function newSystemUser( $name, $options = [] ) {
806  $options += [
807  'validate' => UserNameUtils::RIGOR_VALID,
808  'create' => true,
809  'steal' => false,
810  ];
811 
812  // Username validation
813  // Backwards compatibility with strings / false
814  $validationLevels = [
815  'valid' => UserNameUtils::RIGOR_VALID,
816  'usable' => UserNameUtils::RIGOR_USABLE,
817  'creatable' => UserNameUtils::RIGOR_CREATABLE
818  ];
819  $validate = $options['validate'];
820 
821  // @phan-suppress-next-line PhanSuspiciousValueComparison
822  if ( $validate === false ) {
823  $validation = UserNameUtils::RIGOR_NONE;
824  } elseif ( array_key_exists( $validate, $validationLevels ) ) {
825  $validation = $validationLevels[ $validate ];
826  } else {
827  // Not a recognized value, probably a test for unsupported validation
828  // levels, regardless, just pass it along
829  $validation = $validate;
830  }
831 
832  if ( $validation !== UserNameUtils::RIGOR_VALID ) {
834  __METHOD__ . ' options["validation"] parameter must be omitted or set to "valid".',
835  '1.36'
836  );
837  }
838  $services = MediaWikiServices::getInstance();
839  $userNameUtils = $services->getUserNameUtils();
840 
841  $name = $userNameUtils->getCanonical( (string)$name, $validation );
842  if ( $name === false ) {
843  return null;
844  }
845 
846  $loadBalancer = $services->getDBLoadBalancer();
847  $dbr = $loadBalancer->getConnectionRef( DB_REPLICA );
848 
849  $userQuery = self::getQueryInfo();
850  $row = $dbr->selectRow(
851  $userQuery['tables'],
852  $userQuery['fields'],
853  [ 'user_name' => $name ],
854  __METHOD__,
855  [],
856  $userQuery['joins']
857  );
858  if ( !$row ) {
859  // Try the primary database...
860  $dbw = $loadBalancer->getConnectionRef( DB_PRIMARY );
861  $row = $dbw->selectRow(
862  $userQuery['tables'],
863  $userQuery['fields'],
864  [ 'user_name' => $name ],
865  __METHOD__,
866  [],
867  $userQuery['joins']
868  );
869  }
870 
871  if ( !$row ) {
872  // No user. Create it?
873  if ( !$options['create'] ) {
874  // No.
875  return null;
876  }
877 
878  // If it's a reserved user that had an anonymous actor created for it at
879  // some point, we need special handling.
880  return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
881  return MediaWikiServices::getInstance()->getActorStore()->acquireSystemActorId( $actor, $dbw );
882  }, $name, [ 'token' => self::INVALID_TOKEN ] );
883  }
884 
885  $user = self::newFromRow( $row );
886 
887  if ( !$user->isSystemUser() ) {
888  // User exists. Steal it?
889  if ( !$options['steal'] ) {
890  return null;
891  }
892 
893  $services->getAuthManager()->revokeAccessForUser( $name );
894 
895  $user->invalidateEmail();
896  $user->mToken = self::INVALID_TOKEN;
897  $user->saveSettings();
898  SessionManager::singleton()->preventSessionsForUser( $user->getName() );
899  }
900 
901  return $user;
902  }
903 
905  // endregion -- end of newFrom*() static factory methods
906 
912  public static function whoIs( $id ) {
913  return UserCache::singleton()->getProp( $id, 'name' );
914  }
915 
922  public static function whoIsReal( $id ) {
923  return UserCache::singleton()->getProp( $id, 'real_name' );
924  }
925 
933  public static function idFromName( $name, $flags = self::READ_NORMAL ) {
934  $actor = MediaWikiServices::getInstance()
935  ->getUserIdentityLookup()
936  ->getUserIdentityByName( (string)$name, $flags );
937  if ( $actor && $actor->getId() ) {
938  return $actor->getId();
939  }
940  return null;
941  }
942 
949  public static function resetIdByNameCache() {
950  // TODO: when removing this call, also remove the ActorStore::clearCaches() method!
951  wfDeprecated( __METHOD__, '1.37' );
952  MediaWikiServices::getInstance()->getActorStore()->clearCaches();
953  }
954 
974  public static function isIP( $name ) {
975  wfDeprecated( __METHOD__, '1.35' );
976  return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name )
977  || IPUtils::isIPv6( $name );
978  }
979 
989  public function isIPRange() {
990  wfDeprecated( __METHOD__, '1.35' );
991  return IPUtils::isValidRange( $this->mName );
992  }
993 
1008  public static function isValidUserName( $name ) {
1009  wfDeprecated( __METHOD__, '1.35' );
1010  return MediaWikiServices::getInstance()->getUserNameUtils()->isValid( $name );
1011  }
1012 
1027  public static function isUsableName( $name ) {
1028  wfDeprecated( __METHOD__, '1.35' );
1029  return MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $name );
1030  }
1031 
1042  public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
1043  if ( $groups === [] ) {
1044  return UserArrayFromResult::newFromIDs( [] );
1045  }
1046 
1047  $groups = array_unique( (array)$groups );
1048  $limit = min( 5000, $limit );
1049 
1050  $conds = [ 'ug_group' => $groups ];
1051  if ( $after !== null ) {
1052  $conds[] = 'ug_user > ' . (int)$after;
1053  }
1054 
1055  $dbr = wfGetDB( DB_REPLICA );
1056  $ids = $dbr->selectFieldValues(
1057  'user_groups',
1058  'ug_user',
1059  $conds,
1060  __METHOD__,
1061  [
1062  'DISTINCT' => true,
1063  'ORDER BY' => 'ug_user',
1064  'LIMIT' => $limit,
1065  ]
1066  ) ?: [];
1067  return UserArray::newFromIDs( $ids );
1068  }
1069 
1085  public static function isCreatableName( $name ) {
1086  wfDeprecated( __METHOD__, '1.35' );
1087  return MediaWikiServices::getInstance()->getUserNameUtils()->isCreatable( $name );
1088  }
1089 
1096  public function isValidPassword( $password ) {
1097  // simple boolean wrapper for checkPasswordValidity
1098  return $this->checkPasswordValidity( $password )->isGood();
1099  }
1100 
1122  public function checkPasswordValidity( $password ) {
1123  global $wgPasswordPolicy;
1124 
1125  $upp = new UserPasswordPolicy(
1126  $wgPasswordPolicy['policies'],
1127  $wgPasswordPolicy['checks']
1128  );
1129 
1130  $status = Status::newGood( [] );
1131  $result = false; // init $result to false for the internal checks
1132 
1133  if ( !$this->getHookRunner()->onIsValidPassword( $password, $result, $this ) ) {
1134  $status->error( $result );
1135  return $status;
1136  }
1137 
1138  if ( $result === false ) {
1139  $status->merge( $upp->checkUserPassword( $this, $password ), true );
1140  return $status;
1141  }
1142 
1143  if ( $result === true ) {
1144  return $status;
1145  }
1146 
1147  $status->error( $result );
1148  return $status; // the isValidPassword hook set a string $result and returned true
1149  }
1150 
1168  public static function getCanonicalName( $name, $validate = 'valid' ) {
1169  wfDeprecated( __METHOD__, '1.35' );
1170  // Backwards compatibility with strings / false
1171  $validationLevels = [
1172  'valid' => UserNameUtils::RIGOR_VALID,
1173  'usable' => UserNameUtils::RIGOR_USABLE,
1174  'creatable' => UserNameUtils::RIGOR_CREATABLE
1175  ];
1176 
1177  if ( $validate === false ) {
1178  $validation = UserNameUtils::RIGOR_NONE;
1179  } elseif ( array_key_exists( $validate, $validationLevels ) ) {
1180  $validation = $validationLevels[ $validate ];
1181  } else {
1182  // Not a recognized value, probably a test for unsupported validation
1183  // levels, regardless, just pass it along
1184  $validation = $validate;
1185  }
1186 
1187  return MediaWikiServices::getInstance()
1188  ->getUserNameUtils()
1189  ->getCanonical( (string)$name, $validation );
1190  }
1191 
1201  public function loadDefaults( $name = false, $actorId = null ) {
1202  $this->mId = 0;
1203  $this->mName = $name;
1204  $this->mActorId = $actorId;
1205  $this->mRealName = '';
1206  $this->mEmail = '';
1207 
1208  $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
1209  ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1210  if ( $loggedOut !== 0 ) {
1211  $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
1212  } else {
1213  $this->mTouched = '1'; # Allow any pages to be cached
1214  }
1215 
1216  $this->mToken = null; // Don't run cryptographic functions till we need a token
1217  $this->mEmailAuthenticated = null;
1218  $this->mEmailToken = '';
1219  $this->mEmailTokenExpires = null;
1220  $this->mRegistration = wfTimestamp( TS_MW );
1221 
1222  $this->getHookRunner()->onUserLoadDefaults( $this, $name );
1223  }
1224 
1237  public function isItemLoaded( $item, $all = 'all' ) {
1238  return ( $this->mLoadedItems === true && $all === 'all' ) ||
1239  ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1240  }
1241 
1249  public function setItemLoaded( $item ) {
1250  if ( is_array( $this->mLoadedItems ) ) {
1251  $this->mLoadedItems[$item] = true;
1252  }
1253  }
1254 
1260  private function loadFromSession() {
1261  // MediaWiki\Session\Session already did the necessary authentication of the user
1262  // returned here, so just use it if applicable.
1263  $session = $this->getRequest()->getSession();
1264  $user = $session->getUser();
1265  if ( $user->isRegistered() ) {
1266  $this->loadFromUserObject( $user );
1267 
1268  // Other code expects these to be set in the session, so set them.
1269  $session->set( 'wsUserID', $this->getId() );
1270  $session->set( 'wsUserName', $this->getName() );
1271  $session->set( 'wsToken', $this->getToken() );
1272 
1273  return true;
1274  }
1275 
1276  return false;
1277  }
1278 
1286  public function loadFromDatabase( $flags = self::READ_LATEST ) {
1287  // Paranoia
1288  $this->mId = intval( $this->mId );
1289 
1290  if ( !$this->mId ) {
1291  // Anonymous users are not in the database
1292  $this->loadDefaults();
1293  return false;
1294  }
1295 
1296  list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
1297  $db = wfGetDB( $index );
1298 
1299  $userQuery = self::getQueryInfo();
1300  $s = $db->selectRow(
1301  $userQuery['tables'],
1302  $userQuery['fields'],
1303  [ 'user_id' => $this->mId ],
1304  __METHOD__,
1305  $options,
1306  $userQuery['joins']
1307  );
1308 
1309  $this->queryFlagsUsed = $flags;
1310 
1311  // hook is hard deprecated since 1.37
1312  $this->getHookRunner()->onUserLoadFromDatabase( $this, $s );
1313 
1314  if ( $s !== false ) {
1315  // Initialise user table data
1316  $this->loadFromRow( $s );
1317  return true;
1318  }
1319 
1320  // Invalid user_id
1321  $this->mId = 0;
1322  $this->loadDefaults( 'Unknown user' );
1323 
1324  return false;
1325  }
1326 
1338  protected function loadFromRow( $row, $data = null ) {
1339  if ( !is_object( $row ) ) {
1340  throw new InvalidArgumentException( '$row must be an object' );
1341  }
1342 
1343  $all = true;
1344 
1345  if ( isset( $row->actor_id ) ) {
1346  $this->mActorId = (int)$row->actor_id;
1347  if ( $this->mActorId !== 0 ) {
1348  $this->mFrom = 'actor';
1349  }
1350  $this->setItemLoaded( 'actor' );
1351  } else {
1352  $all = false;
1353  }
1354 
1355  if ( isset( $row->user_name ) && $row->user_name !== '' ) {
1356  $this->mName = $row->user_name;
1357  $this->mFrom = 'name';
1358  $this->setItemLoaded( 'name' );
1359  } else {
1360  $all = false;
1361  }
1362 
1363  if ( isset( $row->user_real_name ) ) {
1364  $this->mRealName = $row->user_real_name;
1365  $this->setItemLoaded( 'realname' );
1366  } else {
1367  $all = false;
1368  }
1369 
1370  if ( isset( $row->user_id ) ) {
1371  $this->mId = intval( $row->user_id );
1372  if ( $this->mId !== 0 ) {
1373  $this->mFrom = 'id';
1374  }
1375  $this->setItemLoaded( 'id' );
1376  } else {
1377  $all = false;
1378  }
1379 
1380  if ( isset( $row->user_editcount ) ) {
1381  // Sanity check - don't try to set edit count for anonymous users
1382  // We check the id here and not in UserEditTracker because calling
1383  // User::getId() can trigger some other loading. This will result in
1384  // discarding the user_editcount field for rows if the id wasn't set.
1385  if ( $this->mId !== null && $this->mId !== 0 ) {
1386  MediaWikiServices::getInstance()
1387  ->getUserEditTracker()
1388  ->setCachedUserEditCount( $this, (int)$row->user_editcount );
1389  }
1390  } else {
1391  $all = false;
1392  }
1393 
1394  if ( isset( $row->user_touched ) ) {
1395  $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
1396  } else {
1397  $all = false;
1398  }
1399 
1400  if ( isset( $row->user_token ) ) {
1401  // The definition for the column is binary(32), so trim the NULs
1402  // that appends. The previous definition was char(32), so trim
1403  // spaces too.
1404  $this->mToken = rtrim( $row->user_token, " \0" );
1405  if ( $this->mToken === '' ) {
1406  $this->mToken = null;
1407  }
1408  } else {
1409  $all = false;
1410  }
1411 
1412  if ( isset( $row->user_email ) ) {
1413  $this->mEmail = $row->user_email;
1414  $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1415  $this->mEmailToken = $row->user_email_token;
1416  $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1417  $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
1418  } else {
1419  $all = false;
1420  }
1421 
1422  if ( $all ) {
1423  $this->mLoadedItems = true;
1424  }
1425 
1426  if ( is_array( $data ) ) {
1427 
1428  if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1429  MediaWikiServices::getInstance()
1430  ->getUserGroupManager()
1431  ->loadGroupMembershipsFromArray(
1432  $this,
1433  $data['user_groups'],
1434  $this->queryFlagsUsed
1435  );
1436  }
1437  }
1438  }
1439 
1445  protected function loadFromUserObject( $user ) {
1446  $user->load();
1447  foreach ( self::$mCacheVars as $var ) {
1448  $this->$var = $user->$var;
1449  }
1450  }
1451 
1468  public function addAutopromoteOnceGroups( $event ) {
1469  wfDeprecated( __METHOD__, '1.35' );
1470  return MediaWikiServices::getInstance()
1471  ->getUserGroupManager()
1472  ->addUserToAutopromoteOnceGroups( $this, $event );
1473  }
1474 
1484  protected function makeUpdateConditions( IDatabase $db, array $conditions ) {
1485  if ( $this->mTouched ) {
1486  // CAS check: only update if the row wasn't changed sicne it was loaded.
1487  $conditions['user_touched'] = $db->timestamp( $this->mTouched );
1488  }
1489 
1490  return $conditions;
1491  }
1492 
1503  public function checkAndSetTouched() {
1504  $this->load();
1505 
1506  if ( !$this->mId ) {
1507  return false; // anon
1508  }
1509 
1510  // Get a new user_touched that is higher than the old one
1511  $newTouched = $this->newTouchedTimestamp();
1512 
1513  $dbw = wfGetDB( DB_PRIMARY );
1514  $dbw->update( 'user',
1515  [ 'user_touched' => $dbw->timestamp( $newTouched ) ],
1516  $this->makeUpdateConditions( $dbw, [
1517  'user_id' => $this->mId,
1518  ] ),
1519  __METHOD__
1520  );
1521  $success = ( $dbw->affectedRows() > 0 );
1522 
1523  if ( $success ) {
1524  $this->mTouched = $newTouched;
1525  $this->clearSharedCache( 'changed' );
1526  } else {
1527  // Clears on failure too since that is desired if the cache is stale
1528  $this->clearSharedCache( 'refresh' );
1529  }
1530 
1531  return $success;
1532  }
1533 
1541  public function clearInstanceCache( $reloadFrom = false ) {
1542  global $wgFullyInitialised;
1543 
1544  $this->mDatePreference = null;
1545  $this->mBlockedby = -1; # Unset
1546  $this->mHash = false;
1547  $this->mThisAsAuthority = null;
1548 
1549  if ( $wgFullyInitialised && $this->mFrom ) {
1550  $services = MediaWikiServices::getInstance();
1551  $services->getPermissionManager()->invalidateUsersRightsCache( $this );
1552  $services->getUserOptionsManager()->clearUserOptionsCache( $this );
1553  $services->getTalkPageNotificationManager()->clearInstanceCache( $this );
1554  $services->getUserGroupManager()->clearCache( $this );
1555  $services->getUserEditTracker()->clearUserEditCache( $this );
1556  }
1557 
1558  if ( $reloadFrom ) {
1559  $this->mLoadedItems = [];
1560  $this->mFrom = $reloadFrom;
1561  }
1562  }
1563 
1571  public static function getDefaultOptions() {
1572  wfDeprecated( __METHOD__, '1.35' );
1573  return MediaWikiServices::getInstance()
1574  ->getUserOptionsLookup()
1575  ->getDefaultOptions();
1576  }
1577 
1585  public static function getDefaultOption( $opt ) {
1586  wfDeprecated( __METHOD__, '1.35' );
1587  return MediaWikiServices::getInstance()
1588  ->getUserOptionsLookup()
1589  ->getDefaultOption( $opt );
1590  }
1591 
1603  private function getBlockedStatus( $fromReplica = true, $disableIpBlockExemptChecking = false ) {
1604  if ( $this->mBlockedby != -1 ) {
1605  return;
1606  }
1607 
1608  wfDebug( __METHOD__ . ": checking blocked status for " . $this->getName() );
1609 
1610  // Initialize data...
1611  // Otherwise something ends up stomping on $this->mBlockedby when
1612  // things get lazy-loaded later, causing false positive block hits
1613  // due to -1 !== 0. Probably session-related... Nothing should be
1614  // overwriting mBlockedby, surely?
1615  $this->load();
1616 
1617  // TODO: Block checking shouldn't really be done from the User object. Block
1618  // checking can involve checking for IP blocks, cookie blocks, and/or XFF blocks,
1619  // which need more knowledge of the request context than the User should have.
1620  // Since we do currently check blocks from the User, we have to do the following
1621  // here:
1622  // - Check if this is the user associated with the main request
1623  // - If so, pass the relevant request information to the block manager
1624  $request = null;
1625  if ( $this->isGlobalSessionUser() ) {
1626  // This is the global user, so we need to pass the request
1627  $request = $this->getRequest();
1628  }
1629 
1630  $block = MediaWikiServices::getInstance()->getBlockManager()->getUserBlock(
1631  $this,
1632  $request,
1633  $fromReplica,
1634  $disableIpBlockExemptChecking
1635  );
1636 
1637  if ( $block ) {
1638  $this->mBlock = $block;
1639  $this->mBlockedby = $block->getByName();
1640  $this->mBlockreason = $block->getReason();
1641  $this->mHideName = $block->getHideName();
1642  $this->mAllowUsertalk = $block->isUsertalkEditAllowed();
1643  } else {
1644  $this->mBlock = null;
1645  $this->mBlockedby = '';
1646  $this->mBlockreason = '';
1647  $this->mHideName = 0;
1648  $this->mAllowUsertalk = false;
1649  }
1650  }
1651 
1657  public function isPingLimitable() {
1658  global $wgRateLimitsExcludedIPs;
1659  if ( IPUtils::isInRanges( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
1660  // No other good way currently to disable rate limits
1661  // for specific IPs. :P
1662  // But this is a crappy hack and should die.
1663  return false;
1664  }
1665  return !$this->isAllowed( 'noratelimit' );
1666  }
1667 
1684  public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1685  $logger = LoggerFactory::getInstance( 'ratelimit' );
1686 
1687  // Call the 'PingLimiter' hook
1688  $result = false;
1689  if ( !$this->getHookRunner()->onPingLimiter( $this, $action, $result, $incrBy ) ) {
1690  return $result;
1691  }
1692 
1693  global $wgRateLimits;
1694  if ( !isset( $wgRateLimits[$action] ) ) {
1695  return false;
1696  }
1697 
1698  $limits = array_merge(
1699  [ '&can-bypass' => true ],
1700  $wgRateLimits[$action]
1701  );
1702 
1703  // Some groups shouldn't trigger the ping limiter, ever
1704  if ( $limits['&can-bypass'] && !$this->isPingLimitable() ) {
1705  return false;
1706  }
1707 
1708  $logger->debug( __METHOD__ . ": limiting $action rate for {$this->getName()}" );
1709 
1710  $keys = [];
1711  $id = $this->getId();
1712  $isNewbie = $this->isNewbie();
1714 
1715  if ( $id == 0 ) {
1716  // "shared anon" limit, for all anons combined
1717  if ( isset( $limits['anon'] ) ) {
1718  $keys[$cache->makeKey( 'limiter', $action, 'anon' )] = $limits['anon'];
1719  }
1720  } else {
1721  // "global per name" limit, across sites
1722  if ( isset( $limits['user-global'] ) ) {
1723  $lookup = MediaWikiServices::getInstance()
1724  ->getCentralIdLookupFactory()
1725  ->getNonLocalLookup();
1726 
1727  $centralId = $lookup
1728  ? $lookup->centralIdFromLocalUser( $this, CentralIdLookup::AUDIENCE_RAW )
1729  : 0;
1730 
1731  if ( $centralId ) {
1732  // We don't have proper realms, use provider ID.
1733  $realm = $lookup->getProviderId();
1734 
1735  $globalKey = $cache->makeGlobalKey( 'limiter', $action, 'user-global',
1736  $realm, $centralId );
1737  } else {
1738  // Fall back to a local key for a local ID
1739  $globalKey = $cache->makeKey( 'limiter', $action, 'user-global',
1740  'local', $id );
1741  }
1742  $keys[$globalKey] = $limits['user-global'];
1743  }
1744  }
1745 
1746  if ( $isNewbie ) {
1747  // "per ip" limit for anons and newbie users
1748  if ( isset( $limits['ip'] ) ) {
1749  $ip = $this->getRequest()->getIP();
1750  $keys[$cache->makeGlobalKey( 'limiter', $action, 'ip', $ip )] = $limits['ip'];
1751  }
1752  // "per subnet" limit for anons and newbie users
1753  if ( isset( $limits['subnet'] ) ) {
1754  $ip = $this->getRequest()->getIP();
1755  $subnet = IPUtils::getSubnet( $ip );
1756  if ( $subnet !== false ) {
1757  $keys[$cache->makeGlobalKey( 'limiter', $action, 'subnet', $subnet )] = $limits['subnet'];
1758  }
1759  }
1760  }
1761 
1762  // determine the "per user account" limit
1763  $userLimit = false;
1764  if ( $id !== 0 && isset( $limits['user'] ) ) {
1765  // default limit for logged-in users
1766  $userLimit = $limits['user'];
1767  }
1768  // limits for newbie logged-in users (overrides all the normal user limits)
1769  if ( $id !== 0 && $isNewbie && isset( $limits['newbie'] ) ) {
1770  $userLimit = $limits['newbie'];
1771  } else {
1772  // Check for group-specific limits
1773  // If more than one group applies, use the highest allowance (if higher than the default)
1774  $userGroups = MediaWikiServices::getInstance()->getUserGroupManager()->getUserGroups( $this );
1775  foreach ( $userGroups as $group ) {
1776  if ( isset( $limits[$group] ) ) {
1777  if ( $userLimit === false
1778  || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
1779  ) {
1780  $userLimit = $limits[$group];
1781  }
1782  }
1783  }
1784  }
1785 
1786  // Set the user limit key
1787  if ( $userLimit !== false ) {
1788  // phan is confused because &can-bypass's value is a bool, so it assumes
1789  // that $userLimit is also a bool here.
1790  // @phan-suppress-next-line PhanTypeInvalidExpressionArrayDestructuring
1791  list( $max, $period ) = $userLimit;
1792  $logger->debug( __METHOD__ . ": effective user limit: $max in {$period}s" );
1793  $keys[$cache->makeKey( 'limiter', $action, 'user', $id )] = $userLimit;
1794  }
1795 
1796  // ip-based limits for all ping-limitable users
1797  if ( isset( $limits['ip-all'] ) ) {
1798  $ip = $this->getRequest()->getIP();
1799  // ignore if user limit is more permissive
1800  if ( $isNewbie || $userLimit === false
1801  || $limits['ip-all'][0] / $limits['ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
1802  $keys[$cache->makeGlobalKey( 'limiter', $action, 'ip-all', $ip )] = $limits['ip-all'];
1803  }
1804  }
1805 
1806  // subnet-based limits for all ping-limitable users
1807  if ( isset( $limits['subnet-all'] ) ) {
1808  $ip = $this->getRequest()->getIP();
1809  $subnet = IPUtils::getSubnet( $ip );
1810  if ( $subnet !== false ) {
1811  // ignore if user limit is more permissive
1812  if ( $isNewbie || $userLimit === false
1813  || $limits['ip-all'][0] / $limits['ip-all'][1]
1814  > $userLimit[0] / $userLimit[1] ) {
1815  $keys[$cache->makeGlobalKey( 'limiter', $action, 'subnet-all', $subnet )] = $limits['subnet-all'];
1816  }
1817  }
1818  }
1819 
1820  // XXX: We may want to use $cache->getCurrentTime() here, but that would make it
1821  // harder to test for T246991. Also $cache->getCurrentTime() is documented
1822  // as being for testing only, so it apparently should not be called here.
1823  $now = MWTimestamp::time();
1824  $clockFudge = 3; // avoid log spam when a clock is slightly off
1825 
1826  $triggered = false;
1827  foreach ( $keys as $key => $limit ) {
1828 
1829  // Do the update in a merge callback, for atomicity.
1830  // To use merge(), we need to explicitly track the desired expiry timestamp.
1831  // This tracking was introduced to investigate T246991. Once it is no longer needed,
1832  // we could go back to incrWithInit(), though that has more potential for race
1833  // conditions between the get() and incrWithInit() calls.
1834  $cache->merge(
1835  $key,
1836  function ( $cache, $key, $data, &$expiry )
1837  use ( $action, $logger, &$triggered, $now, $clockFudge, $limit, $incrBy )
1838  {
1839  // phan is confused because &can-bypass's value is a bool, so it assumes
1840  // that $userLimit is also a bool here.
1841  // @phan-suppress-next-line PhanTypeInvalidExpressionArrayDestructuring
1842  list( $max, $period ) = $limit;
1843 
1844  $expiry = $now + (int)$period;
1845  $count = 0;
1846 
1847  // Already pinged?
1848  if ( $data ) {
1849  // NOTE: in order to investigate T246991, we write the expiry time
1850  // into the payload, along with the count.
1851  $fields = explode( '|', $data );
1852  $storedCount = (int)( $fields[0] ?? 0 );
1853  $storedExpiry = (int)( $fields[1] ?? PHP_INT_MAX );
1854 
1855  // Found a stale entry. This should not happen!
1856  if ( $storedExpiry < ( $now + $clockFudge ) ) {
1857  $logger->info(
1858  'User::pingLimiter: '
1859  . 'Stale rate limit entry, cache key failed to expire (T246991)',
1860  [
1861  'action' => $action,
1862  'user' => $this->getName(),
1863  'limit' => $max,
1864  'period' => $period,
1865  'count' => $storedCount,
1866  'key' => $key,
1867  'expiry' => MWTimestamp::convert( TS_DB, $storedExpiry ),
1868  ]
1869  );
1870  } else {
1871  // NOTE: We'll keep the original expiry when bumping counters,
1872  // resulting in a kind of fixed-window throttle.
1873  $expiry = min( $storedExpiry, $now + (int)$period );
1874  $count = $storedCount;
1875  }
1876  }
1877 
1878  // Limit exceeded!
1879  if ( $count >= $max ) {
1880  if ( !$triggered ) {
1881  $logger->info(
1882  'User::pingLimiter: User tripped rate limit',
1883  [
1884  'action' => $action,
1885  'user' => $this->getName(),
1886  'ip' => $this->getRequest()->getIP(),
1887  'limit' => $max,
1888  'period' => $period,
1889  'count' => $count,
1890  'key' => $key
1891  ]
1892  );
1893  }
1894 
1895  $triggered = true;
1896  }
1897 
1898  $count += $incrBy;
1899  $data = "$count|$expiry";
1900  return $data;
1901  }
1902  );
1903  }
1904 
1905  return $triggered;
1906  }
1907 
1920  public function isBlocked( $fromReplica = true ) {
1921  return $this->getBlock( $fromReplica ) !== null;
1922  }
1923 
1936  public function getBlock(
1937  $freshness = self::READ_NORMAL,
1938  $disableIpBlockExemptChecking = false
1939  ): ?Block {
1940  if ( is_bool( $freshness ) ) {
1941  $fromReplica = $freshness;
1942  } else {
1943  $fromReplica = ( $freshness !== self::READ_LATEST );
1944  }
1945 
1946  $this->getBlockedStatus( $fromReplica, $disableIpBlockExemptChecking );
1947  return $this->mBlock instanceof AbstractBlock ? $this->mBlock : null;
1948  }
1949 
1961  public function isBlockedFrom( $title, $fromReplica = false ) {
1962  // TODO: remove the cast when PermissionManager accepts PageIdentity
1964  return MediaWikiServices::getInstance()->getPermissionManager()
1965  ->isBlockedFrom( $this, $title, $fromReplica );
1966  }
1967 
1972  public function blockedBy() {
1973  $this->getBlockedStatus();
1974  return $this->mBlockedby;
1975  }
1976 
1983  public function blockedFor() {
1984  $this->getBlockedStatus();
1985  return $this->mBlockreason;
1986  }
1987 
1992  public function getBlockId() {
1993  $this->getBlockedStatus();
1994  return ( $this->mBlock ? $this->mBlock->getId() : false );
1995  }
1996 
2005  public function isBlockedGlobally( $ip = '' ) {
2006  return $this->getGlobalBlock( $ip ) instanceof AbstractBlock;
2007  }
2008 
2019  public function getGlobalBlock( $ip = '' ) {
2020  if ( $this->mGlobalBlock !== null ) {
2021  return $this->mGlobalBlock ?: null;
2022  }
2023  // User is already an IP?
2024  if ( IPUtils::isIPAddress( $this->getName() ) ) {
2025  $ip = $this->getName();
2026  } elseif ( !$ip ) {
2027  $ip = $this->getRequest()->getIP();
2028  }
2029  $blocked = false;
2030  $block = null;
2031  $this->getHookRunner()->onUserIsBlockedGlobally( $this, $ip, $blocked, $block );
2032 
2033  if ( $blocked && $block === null ) {
2034  // back-compat: UserIsBlockedGlobally didn't have $block param first
2035  $block = new SystemBlock( [
2036  'address' => $ip,
2037  'systemBlock' => 'global-block'
2038  ] );
2039  }
2040 
2041  $this->mGlobalBlock = $blocked ? $block : false;
2042  return $this->mGlobalBlock ?: null;
2043  }
2044 
2050  public function isLocked() {
2051  if ( $this->mLocked !== null ) {
2052  return $this->mLocked;
2053  }
2054  // Reset for hook
2055  $this->mLocked = false;
2056  $this->getHookRunner()->onUserIsLocked( $this, $this->mLocked );
2057  return $this->mLocked;
2058  }
2059 
2065  public function isHidden() {
2066  if ( $this->mHideName !== null ) {
2067  return (bool)$this->mHideName;
2068  }
2069  $this->getBlockedStatus();
2070  return (bool)$this->mHideName;
2071  }
2072 
2078  public function getId( $wikiId = self::LOCAL ): int {
2079  $this->deprecateInvalidCrossWiki( $wikiId, '1.36' );
2080  if ( $this->mId === null && $this->mName !== null ) {
2081  $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
2082  if ( $userNameUtils->isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) ) {
2083  // Special case, we know the user is anonymous
2084  // Note that "external" users are "local" (they have an actor ID that is relative to
2085  // the local wiki).
2086  return 0;
2087  }
2088  }
2089 
2090  if ( !$this->isItemLoaded( 'id' ) ) {
2091  // Don't load if this was initialized from an ID
2092  $this->load();
2093  }
2094 
2095  return (int)$this->mId;
2096  }
2097 
2102  public function setId( $v ) {
2103  $this->mId = $v;
2104  $this->clearInstanceCache( 'id' );
2105  }
2106 
2111  public function getName(): string {
2112  if ( $this->isItemLoaded( 'name', 'only' ) ) {
2113  // Special case optimisation
2114  return $this->mName;
2115  }
2116 
2117  $this->load();
2118  if ( $this->mName === false ) {
2119  // Clean up IPs
2120  $this->mName = IPUtils::sanitizeIP( $this->getRequest()->getIP() );
2121  }
2122 
2123  return $this->mName;
2124  }
2125 
2139  public function setName( $str ) {
2140  $this->load();
2141  $this->mName = $str;
2142  }
2143 
2157  public function getActorId( $dbwOrWikiId = self::LOCAL ): int {
2158  if ( $dbwOrWikiId ) {
2159  wfDeprecatedMsg( 'Passing a parameter to getActorId() is deprecated', '1.36' );
2160  }
2161 
2162  if ( is_string( $dbwOrWikiId ) ) {
2163  $this->assertWiki( $dbwOrWikiId );
2164  }
2165 
2166  if ( !$this->isItemLoaded( 'actor' ) ) {
2167  $this->load();
2168  }
2169 
2170  if ( !$this->mActorId && $dbwOrWikiId instanceof IDatabase ) {
2171  MediaWikiServices::getInstance()
2172  ->getActorStoreFactory()
2173  ->getActorNormalization( $dbwOrWikiId->getDomainID() )
2174  ->acquireActorId( $this, $dbwOrWikiId );
2175  // acquireActorId will call setActorId on $this
2176  Assert::postcondition(
2177  $this->mActorId !== null,
2178  "Failed to acquire actor ID for user id {$this->mId} name {$this->mName}"
2179  );
2180  }
2181 
2182  return (int)$this->mActorId;
2183  }
2184 
2194  public function setActorId( int $actorId ) {
2195  $this->mActorId = $actorId;
2196  $this->setItemLoaded( 'actor' );
2197  }
2198 
2203  public function getTitleKey() {
2204  return str_replace( ' ', '_', $this->getName() );
2205  }
2206 
2213  private function newTouchedTimestamp() {
2214  $time = time();
2215  if ( $this->mTouched ) {
2216  $time = max( $time, wfTimestamp( TS_UNIX, $this->mTouched ) + 1 );
2217  }
2218 
2219  return wfTimestamp( TS_MW, $time );
2220  }
2221 
2232  public function clearSharedCache( $mode = 'refresh' ) {
2233  if ( !$this->getId() ) {
2234  return;
2235  }
2236 
2237  $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
2238  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2239  $key = $this->getCacheKey( $cache );
2240 
2241  if ( $mode === 'refresh' ) {
2242  $cache->delete( $key, 1 ); // low tombstone/"hold-off" TTL
2243  } else {
2244  $lb->getConnectionRef( DB_PRIMARY )->onTransactionPreCommitOrIdle(
2245  static function () use ( $cache, $key ) {
2246  $cache->delete( $key );
2247  },
2248  __METHOD__
2249  );
2250  }
2251  }
2252 
2258  public function invalidateCache() {
2259  $this->touch();
2260  $this->clearSharedCache( 'changed' );
2261  }
2262 
2275  public function touch() {
2276  $id = $this->getId();
2277  if ( $id ) {
2278  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2279  $key = $cache->makeKey( 'user-quicktouched', 'id', $id );
2280  $cache->touchCheckKey( $key );
2281  $this->mQuickTouched = null;
2282  }
2283  }
2284 
2290  public function validateCache( $timestamp ) {
2291  return ( $timestamp >= $this->getTouched() );
2292  }
2293 
2302  public function getTouched() {
2303  $this->load();
2304 
2305  if ( $this->mId ) {
2306  if ( $this->mQuickTouched === null ) {
2307  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2308  $key = $cache->makeKey( 'user-quicktouched', 'id', $this->mId );
2309 
2310  $this->mQuickTouched = wfTimestamp( TS_MW, $cache->getCheckKeyTime( $key ) );
2311  }
2312 
2313  return max( $this->mTouched, $this->mQuickTouched );
2314  }
2315 
2316  return $this->mTouched;
2317  }
2318 
2324  public function getDBTouched() {
2325  $this->load();
2326 
2327  return $this->mTouched;
2328  }
2329 
2342  public function changeAuthenticationData( array $data ) {
2343  $manager = MediaWikiServices::getInstance()->getAuthManager();
2344  $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2345  $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2346 
2347  $status = Status::newGood( 'ignored' );
2348  foreach ( $reqs as $req ) {
2349  $status->merge( $manager->allowsAuthenticationDataChange( $req ), true );
2350  }
2351  if ( $status->getValue() === 'ignored' ) {
2352  $status->warning( 'authenticationdatachange-ignored' );
2353  }
2354 
2355  if ( $status->isGood() ) {
2356  foreach ( $reqs as $req ) {
2357  $manager->changeAuthenticationData( $req );
2358  }
2359  }
2360  return $status;
2361  }
2362 
2369  public function getToken( $forceCreation = true ) {
2371 
2372  $this->load();
2373  if ( !$this->mToken && $forceCreation ) {
2374  $this->setToken();
2375  }
2376 
2377  if ( !$this->mToken ) {
2378  // The user doesn't have a token, return null to indicate that.
2379  return null;
2380  }
2381 
2382  if ( $this->mToken === self::INVALID_TOKEN ) {
2383  // We return a random value here so existing token checks are very
2384  // likely to fail.
2385  return MWCryptRand::generateHex( self::TOKEN_LENGTH );
2386  }
2387 
2388  if ( $wgAuthenticationTokenVersion === null ) {
2389  // $wgAuthenticationTokenVersion not in use, so return the raw secret
2390  return $this->mToken;
2391  }
2392 
2393  // $wgAuthenticationTokenVersion in use, so hmac it.
2394  $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
2395 
2396  // The raw hash can be overly long. Shorten it up.
2397  $len = max( 32, self::TOKEN_LENGTH );
2398  if ( strlen( $ret ) < $len ) {
2399  // Should never happen, even md5 is 128 bits
2400  throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
2401  }
2402 
2403  return substr( $ret, -$len );
2404  }
2405 
2412  public function setToken( $token = false ) {
2413  $this->load();
2414  if ( $this->mToken === self::INVALID_TOKEN ) {
2415  LoggerFactory::getInstance( 'session' )
2416  ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
2417  } elseif ( !$token ) {
2418  $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
2419  } else {
2420  $this->mToken = $token;
2421  }
2422  }
2423 
2428  public function getEmail(): string {
2429  $this->load();
2430  $email = $this->mEmail;
2431  $this->getHookRunner()->onUserGetEmail( $this, $email );
2432  // In case a hook handler returns e.g. null
2433  $this->mEmail = is_string( $email ) ? $email : '';
2434  return $this->mEmail;
2435  }
2436 
2442  $this->load();
2443  $this->getHookRunner()->onUserGetEmailAuthenticationTimestamp(
2444  $this, $this->mEmailAuthenticated );
2446  }
2447 
2452  public function setEmail( string $str ) {
2453  $this->load();
2454  if ( $str == $this->getEmail() ) {
2455  return;
2456  }
2457  $this->invalidateEmail();
2458  $this->mEmail = $str;
2459  $this->getHookRunner()->onUserSetEmail( $this, $this->mEmail );
2460  }
2461 
2469  public function setEmailWithConfirmation( string $str ) {
2471 
2472  if ( !$wgEnableEmail ) {
2473  return Status::newFatal( 'emaildisabled' );
2474  }
2475 
2476  $oldaddr = $this->getEmail();
2477  if ( $str === $oldaddr ) {
2478  return Status::newGood( true );
2479  }
2480 
2481  $type = $oldaddr != '' ? 'changed' : 'set';
2482  $notificationResult = null;
2483 
2484  if ( $wgEmailAuthentication && $type === 'changed' ) {
2485  // Send the user an email notifying the user of the change in registered
2486  // email address on their previous email address
2487  $change = $str != '' ? 'changed' : 'removed';
2488  $notificationResult = $this->sendMail(
2489  wfMessage( 'notificationemail_subject_' . $change )->text(),
2490  wfMessage( 'notificationemail_body_' . $change,
2491  $this->getRequest()->getIP(),
2492  $this->getName(),
2493  $str )->text()
2494  );
2495  }
2496 
2497  $this->setEmail( $str );
2498 
2499  if ( $str !== '' && $wgEmailAuthentication ) {
2500  // Send a confirmation request to the new address if needed
2501  $result = $this->sendConfirmationMail( $type );
2502 
2503  if ( $notificationResult !== null ) {
2504  $result->merge( $notificationResult );
2505  }
2506 
2507  if ( $result->isGood() ) {
2508  // Say to the caller that a confirmation and notification mail has been sent
2509  $result->value = 'eauth';
2510  }
2511  } else {
2512  $result = Status::newGood( true );
2513  }
2514 
2515  return $result;
2516  }
2517 
2522  public function getRealName(): string {
2523  if ( !$this->isItemLoaded( 'realname' ) ) {
2524  $this->load();
2525  }
2526 
2527  return $this->mRealName;
2528  }
2529 
2534  public function setRealName( string $str ) {
2535  $this->load();
2536  $this->mRealName = $str;
2537  }
2538 
2551  public function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
2552  if ( $oname === null ) {
2553  return null; // b/c
2554  }
2555  return MediaWikiServices::getInstance()
2556  ->getUserOptionsLookup()
2557  ->getOption( $this, $oname, $defaultOverride, $ignoreHidden );
2558  }
2559 
2570  public function getOptions( $flags = 0 ) {
2571  wfDeprecated( __METHOD__, '1.35' );
2572  return MediaWikiServices::getInstance()
2573  ->getUserOptionsLookup()
2574  ->getOptions( $this, $flags );
2575  }
2576 
2585  public function getBoolOption( $oname ) {
2586  return MediaWikiServices::getInstance()
2587  ->getUserOptionsLookup()
2588  ->getBoolOption( $this, $oname );
2589  }
2590 
2600  public function getIntOption( $oname, $defaultOverride = 0 ) {
2601  if ( $oname === null ) {
2602  return null; // b/c
2603  }
2604  return MediaWikiServices::getInstance()
2605  ->getUserOptionsLookup()
2606  ->getIntOption( $this, $oname, $defaultOverride );
2607  }
2608 
2618  public function setOption( $oname, $val ) {
2619  MediaWikiServices::getInstance()
2620  ->getUserOptionsManager()
2621  ->setOption( $this, $oname, $val );
2622  }
2623 
2634  public function getTokenFromOption( $oname ) {
2635  global $wgHiddenPrefs;
2636 
2637  $id = $this->getId();
2638  if ( !$id || in_array( $oname, $wgHiddenPrefs ) ) {
2639  return false;
2640  }
2641 
2642  $token = $this->getOption( $oname );
2643  if ( !$token ) {
2644  // Default to a value based on the user token to avoid space
2645  // wasted on storing tokens for all users. When this option
2646  // is set manually by the user, only then is it stored.
2647  $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
2648  }
2649 
2650  return $token;
2651  }
2652 
2662  public function resetTokenFromOption( $oname ) {
2663  global $wgHiddenPrefs;
2664  if ( in_array( $oname, $wgHiddenPrefs ) ) {
2665  return false;
2666  }
2667 
2668  $token = MWCryptRand::generateHex( 40 );
2669  $this->setOption( $oname, $token );
2670  return $token;
2671  }
2672 
2698  public static function listOptionKinds() {
2699  wfDeprecated( __METHOD__, '1.35' );
2700  return MediaWikiServices::getInstance()
2701  ->getUserOptionsManager()
2702  ->listOptionKinds();
2703  }
2704 
2720  public function getOptionKinds( IContextSource $context, $options = null ) {
2721  wfDeprecated( __METHOD__, '1.35' );
2722  return MediaWikiServices::getInstance()
2723  ->getUserOptionsManager()
2724  ->getOptionKinds( $this, $context, $options );
2725  }
2726 
2743  public function resetOptions(
2744  $resetKinds = [ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ],
2745  IContextSource $context = null
2746  ) {
2747  wfDeprecated( __METHOD__, '1.35' );
2748  MediaWikiServices::getInstance()
2749  ->getUserOptionsManager()
2750  ->resetOptions(
2751  $this,
2752  $context ?? RequestContext::getMain(),
2753  $resetKinds
2754  );
2755  }
2756 
2761  public function getDatePreference() {
2762  // Important migration for old data rows
2763  if ( $this->mDatePreference === null ) {
2764  global $wgLang;
2765  $value = $this->getOption( 'date' );
2766  $map = $wgLang->getDatePreferenceMigrationMap();
2767  if ( isset( $map[$value] ) ) {
2768  $value = $map[$value];
2769  }
2770  $this->mDatePreference = $value;
2771  }
2772  return $this->mDatePreference;
2773  }
2774 
2781  public function requiresHTTPS() {
2782  global $wgForceHTTPS, $wgSecureLogin;
2783  if ( $wgForceHTTPS ) {
2784  return true;
2785  }
2786  if ( !$wgSecureLogin ) {
2787  return false;
2788  }
2789  return $this->getBoolOption( 'prefershttps' );
2790  }
2791 
2797  public function getStubThreshold() {
2798  global $wgMaxArticleSize; # Maximum article size, in KiB
2799  $threshold = $this->getIntOption( 'stubthreshold' );
2800  if ( $threshold > $wgMaxArticleSize * 1024 ) {
2801  // If they have set an impossible value, disable the preference
2802  // so we can use the parser cache again.
2803  $threshold = 0;
2804  }
2805  return $threshold;
2806  }
2807 
2817  public function getRights() {
2818  wfDeprecated( __METHOD__, '1.34' );
2819  return MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissions( $this );
2820  }
2821 
2830  public function getGroups() {
2831  return MediaWikiServices::getInstance()
2832  ->getUserGroupManager()
2833  ->getUserGroups( $this, $this->queryFlagsUsed );
2834  }
2835 
2845  public function getGroupMemberships() {
2846  return MediaWikiServices::getInstance()
2847  ->getUserGroupManager()
2848  ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
2849  }
2850 
2862  public function getEffectiveGroups( $recache = false ) {
2863  wfDeprecated( __METHOD__, '1.35' );
2864  return MediaWikiServices::getInstance()
2865  ->getUserGroupManager()
2866  ->getUserEffectiveGroups( $this, $this->queryFlagsUsed, $recache );
2867  }
2868 
2880  public function getAutomaticGroups( $recache = false ) {
2881  wfDeprecated( __METHOD__, '1.35' );
2882  return MediaWikiServices::getInstance()
2883  ->getUserGroupManager()
2884  ->getUserImplicitGroups( $this, $this->queryFlagsUsed, $recache );
2885  }
2886 
2899  public function getFormerGroups() {
2900  wfDeprecated( __METHOD__, '1.35' );
2901  return MediaWikiServices::getInstance()
2902  ->getUserGroupManager()
2903  ->getUserFormerGroups( $this, $this->queryFlagsUsed );
2904  }
2905 
2910  public function getEditCount() {
2911  return MediaWikiServices::getInstance()
2912  ->getUserEditTracker()
2913  ->getUserEditCount( $this );
2914  }
2915 
2929  public function addGroup( $group, $expiry = null ) {
2930  return MediaWikiServices::getInstance()
2931  ->getUserGroupManager()
2932  ->addUserToGroup( $this, $group, $expiry, true );
2933  }
2934 
2944  public function removeGroup( $group ) {
2945  return MediaWikiServices::getInstance()
2946  ->getUserGroupManager()
2947  ->removeUserFromGroup( $this, $group );
2948  }
2949 
2958  public function isRegistered(): bool {
2959  return $this->getId() != 0;
2960  }
2961 
2968  public function isLoggedIn() {
2969  // Hard-deprecated in 1.37
2970  wfDeprecated( __METHOD__, '1.36' );
2971  return $this->isRegistered();
2972  }
2973 
2978  public function isAnon() {
2979  return !$this->isRegistered();
2980  }
2981 
2986  public function isBot() {
2987  $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager();
2988  if ( in_array( 'bot', $userGroupManager->getUserGroups( $this ) ) && $this->isAllowed( 'bot' ) ) {
2989  return true;
2990  }
2991 
2992  $isBot = false;
2993  $this->getHookRunner()->onUserIsBot( $this, $isBot );
2994 
2995  return $isBot;
2996  }
2997 
3007  public function isSystemUser() {
3008  $this->load();
3009  if ( $this->getEmail() || $this->mToken !== self::INVALID_TOKEN ||
3010  MediaWikiServices::getInstance()->getAuthManager()->userCanAuthenticate( $this->mName )
3011  ) {
3012  return false;
3013  }
3014  return true;
3015  }
3016 
3017  public function isAllowedAny( ...$permissions ): bool {
3018  return $this->getThisAsAuthority()->isAllowedAny( ...$permissions );
3019  }
3020 
3021  public function isAllowedAll( ...$permissions ): bool {
3022  return $this->getThisAsAuthority()->isAllowedAll( ...$permissions );
3023  }
3024 
3025  public function isAllowed( string $permission ): bool {
3026  return $this->getThisAsAuthority()->isAllowed( $permission );
3027  }
3028 
3033  public function useRCPatrol() {
3034  global $wgUseRCPatrol;
3035  return $wgUseRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
3036  }
3037 
3042  public function useNPPatrol() {
3044  return (
3046  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3047  );
3048  }
3049 
3054  public function useFilePatrol() {
3056  return (
3058  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3059  );
3060  }
3061 
3067  public function getRequest() {
3068  if ( $this->mRequest ) {
3069  return $this->mRequest;
3070  }
3071  return RequestContext::getMain()->getRequest();
3072  }
3073 
3084  public function isWatched( PageIdentity $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3085  wfDeprecated( __METHOD__, '1.37' );
3086  $watchlistManager = MediaWikiServices::getInstance()->getWatchlistManager();
3087  if ( $checkRights ) {
3088  return $watchlistManager->isWatched( $this, $title );
3089  }
3090  return $watchlistManager->isWatchedIgnoringRights( $this, $title );
3091  }
3092 
3105  public function isTempWatched( PageIdentity $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3106  wfDeprecated( __METHOD__, '1.37' );
3107  $watchlistManager = MediaWikiServices::getInstance()->getWatchlistManager();
3108  if ( $checkRights ) {
3109  return $watchlistManager->isTempWatched( $this, $title );
3110  }
3111  return $watchlistManager->isTempWatchedIgnoringRights( $this, $title );
3112  }
3113 
3124  public function addWatch(
3126  $checkRights = self::CHECK_USER_RIGHTS,
3127  ?string $expiry = null
3128  ) {
3129  wfDeprecated( __METHOD__, '1.37' );
3130  $watchlistManager = MediaWikiServices::getInstance()->getWatchlistManager();
3131  if ( $checkRights ) {
3132  $watchlistManager->addWatch( $this, $title, $expiry );
3133  } else {
3134  $watchlistManager->addWatchIgnoringRights( $this, $title, $expiry );
3135  }
3136  }
3137 
3147  public function removeWatch( PageIdentity $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3148  wfDeprecated( __METHOD__, '1.37' );
3149  $watchlistManager = MediaWikiServices::getInstance()->getWatchlistManager();
3150  if ( $checkRights ) {
3151  $watchlistManager->removeWatch( $this, $title );
3152  } else {
3153  $watchlistManager->removeWatchIgnoringRights( $this, $title );
3154  }
3155  }
3156 
3162  public function getExperienceLevel() {
3163  global $wgLearnerEdits,
3167 
3168  if ( $this->isAnon() ) {
3169  return false;
3170  }
3171 
3172  $editCount = $this->getEditCount();
3173  $registration = $this->getRegistration();
3174  $now = time();
3175  $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
3176  $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
3177  if ( $registration === null ) {
3178  // for some very old accounts, this information is missing in the database
3179  // treat them as old enough to be 'experienced'
3180  $registration = $experiencedRegistration;
3181  }
3182 
3183  if ( $editCount < $wgLearnerEdits ||
3184  $registration > $learnerRegistration ) {
3185  return 'newcomer';
3186  }
3187 
3188  if ( $editCount > $wgExperiencedUserEdits &&
3189  $registration <= $experiencedRegistration
3190  ) {
3191  return 'experienced';
3192  }
3193 
3194  return 'learner';
3195  }
3196 
3205  public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
3206  $this->load();
3207  if ( $this->mId == 0 ) {
3208  return;
3209  }
3210 
3211  $session = $this->getRequest()->getSession();
3212  if ( $request && $session->getRequest() !== $request ) {
3213  $session = $session->sessionWithRequest( $request );
3214  }
3215  $delay = $session->delaySave();
3216 
3217  if ( !$session->getUser()->equals( $this ) ) {
3218  if ( !$session->canSetUser() ) {
3219  LoggerFactory::getInstance( 'session' )
3220  ->warning( __METHOD__ .
3221  ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3222  );
3223  return;
3224  }
3225  $session->setUser( $this );
3226  }
3227 
3228  $session->setRememberUser( $rememberMe );
3229  if ( $secure !== null ) {
3230  $session->setForceHTTPS( $secure );
3231  }
3232 
3233  $session->persist();
3234 
3235  ScopedCallback::consume( $delay );
3236  }
3237 
3241  public function logout() {
3242  // Avoid PHP 7.1 warning of passing $this by reference
3243  $user = $this;
3244  if ( $this->getHookRunner()->onUserLogout( $user ) ) {
3245  $this->doLogout();
3246  }
3247  }
3248 
3253  public function doLogout() {
3254  $session = $this->getRequest()->getSession();
3255  if ( !$session->canSetUser() ) {
3256  LoggerFactory::getInstance( 'session' )
3257  ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
3258  $error = 'immutable';
3259  } elseif ( !$session->getUser()->equals( $this ) ) {
3260  LoggerFactory::getInstance( 'session' )
3261  ->warning( __METHOD__ .
3262  ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
3263  );
3264  // But we still may as well make this user object anon
3265  $this->clearInstanceCache( 'defaults' );
3266  $error = 'wronguser';
3267  } else {
3268  $this->clearInstanceCache( 'defaults' );
3269  $delay = $session->delaySave();
3270  $session->unpersist(); // Clear cookies (T127436)
3271  $session->setLoggedOutTimestamp( time() );
3272  $session->setUser( new User );
3273  $session->set( 'wsUserID', 0 ); // Other code expects this
3274  $session->resetAllTokens();
3275  ScopedCallback::consume( $delay );
3276  $error = false;
3277  }
3278  LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
3279  'event' => 'logout',
3280  'successful' => $error === false,
3281  'status' => $error ?: 'success',
3282  ] );
3283  }
3284 
3289  public function saveSettings() {
3290  if ( wfReadOnly() ) {
3291  // @TODO: caller should deal with this instead!
3292  // This should really just be an exception.
3294  null,
3295  "Could not update user with ID '{$this->mId}'; DB is read-only."
3296  ) );
3297  return;
3298  }
3299 
3300  $this->load();
3301  if ( $this->mId == 0 ) {
3302  return; // anon
3303  }
3304 
3305  // Get a new user_touched that is higher than the old one.
3306  // This will be used for a CAS check as a last-resort safety
3307  // check against race conditions and replica DB lag.
3308  $newTouched = $this->newTouchedTimestamp();
3309 
3310  $dbw = wfGetDB( DB_PRIMARY );
3311  $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
3312  $dbw->update( 'user',
3313  [ /* SET */
3314  'user_name' => $this->mName,
3315  'user_real_name' => $this->mRealName,
3316  'user_email' => $this->mEmail,
3317  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
3318  'user_touched' => $dbw->timestamp( $newTouched ),
3319  'user_token' => strval( $this->mToken ),
3320  'user_email_token' => $this->mEmailToken,
3321  'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
3322  ], $this->makeUpdateConditions( $dbw, [ /* WHERE */
3323  'user_id' => $this->mId,
3324  ] ), $fname
3325  );
3326 
3327  if ( !$dbw->affectedRows() ) {
3328  // Maybe the problem was a missed cache update; clear it to be safe
3329  $this->clearSharedCache( 'refresh' );
3330  // User was changed in the meantime or loaded with stale data
3331  $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
3332  LoggerFactory::getInstance( 'preferences' )->warning(
3333  "CAS update failed on user_touched for user ID '{user_id}' ({db_flag} read)",
3334  [ 'user_id' => $this->mId, 'db_flag' => $from ]
3335  );
3336  throw new MWException( "CAS update failed on user_touched. " .
3337  "The version of the user to be saved is older than the current version."
3338  );
3339  }
3340 
3341  $dbw->update(
3342  'actor',
3343  [ 'actor_name' => $this->mName ],
3344  [ 'actor_user' => $this->mId ],
3345  $fname
3346  );
3347  MediaWikiServices::getInstance()->getActorStore()->deleteUserIdentityFromCache( $this );
3348  } );
3349 
3350  $this->mTouched = $newTouched;
3351  MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
3352 
3353  $this->getHookRunner()->onUserSaveSettings( $this );
3354  $this->clearSharedCache( 'changed' );
3355  $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
3356  $hcu->purgeTitleUrls( $this->getUserPage(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
3357  }
3358 
3365  public function idForName( $flags = self::READ_NORMAL ) {
3366  $s = trim( $this->getName() );
3367  if ( $s === '' ) {
3368  return 0;
3369  }
3370 
3371  list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
3372  $db = wfGetDB( $index );
3373 
3374  $id = $db->selectField( 'user',
3375  'user_id', [ 'user_name' => $s ], __METHOD__, $options );
3376 
3377  return (int)$id;
3378  }
3379 
3394  public static function createNew( $name, $params = [] ) {
3395  return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
3396  return MediaWikiServices::getInstance()->getActorStore()->createNewActor( $actor, $dbw );
3397  }, $name, $params );
3398  }
3399 
3407  private static function insertNewUser( callable $insertActor, $name, $params = [] ) {
3408  foreach ( [ 'password', 'newpassword', 'newpass_time', 'password_expires' ] as $field ) {
3409  if ( isset( $params[$field] ) ) {
3410  wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
3411  unset( $params[$field] );
3412  }
3413  }
3414 
3415  $user = new User;
3416  $user->load();
3417  $user->setToken(); // init token
3418  if ( isset( $params['options'] ) ) {
3419  MediaWikiServices::getInstance()
3420  ->getUserOptionsManager()
3421  ->loadUserOptions( $user, $user->queryFlagsUsed, $params['options'] );
3422  unset( $params['options'] );
3423  }
3424  $dbw = wfGetDB( DB_PRIMARY );
3425 
3426  $noPass = PasswordFactory::newInvalidPassword()->toString();
3427 
3428  $fields = [
3429  'user_name' => $name,
3430  'user_password' => $noPass,
3431  'user_newpassword' => $noPass,
3432  'user_email' => $user->mEmail,
3433  'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
3434  'user_real_name' => $user->mRealName,
3435  'user_token' => strval( $user->mToken ),
3436  'user_registration' => $dbw->timestamp( $user->mRegistration ),
3437  'user_editcount' => 0,
3438  'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
3439  ];
3440  foreach ( $params as $name => $value ) {
3441  $fields["user_$name"] = $value;
3442  }
3443 
3444  return $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $fields, $insertActor ) {
3445  $dbw->insert( 'user', $fields, $fname, [ 'IGNORE' ] );
3446  if ( $dbw->affectedRows() ) {
3447  $newUser = self::newFromId( $dbw->insertId() );
3448  $newUser->mName = $fields['user_name'];
3449  // Don't pass $this, since calling ::getId, ::getName might force ::load
3450  // and this user might not be ready for the yet.
3451  $newUser->mActorId = $insertActor( new UserIdentityValue( $newUser->mId, $newUser->mName ), $dbw );
3452  // Load the user from master to avoid replica lag
3453  $newUser->load( self::READ_LATEST );
3454  } else {
3455  $newUser = null;
3456  }
3457  return $newUser;
3458  } );
3459  }
3460 
3487  public function addToDatabase() {
3488  $this->load();
3489  if ( !$this->mToken ) {
3490  $this->setToken(); // init token
3491  }
3492 
3493  if ( !is_string( $this->mName ) ) {
3494  throw new RuntimeException( "User name field is not set." );
3495  }
3496 
3497  $this->mTouched = $this->newTouchedTimestamp();
3498 
3499  $dbw = wfGetDB( DB_PRIMARY );
3500  $status = $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) {
3501  $noPass = PasswordFactory::newInvalidPassword()->toString();
3502  $dbw->insert( 'user',
3503  [
3504  'user_name' => $this->mName,
3505  'user_password' => $noPass,
3506  'user_newpassword' => $noPass,
3507  'user_email' => $this->mEmail,
3508  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
3509  'user_real_name' => $this->mRealName,
3510  'user_token' => strval( $this->mToken ),
3511  'user_registration' => $dbw->timestamp( $this->mRegistration ),
3512  'user_editcount' => 0,
3513  'user_touched' => $dbw->timestamp( $this->mTouched ),
3514  ], $fname,
3515  [ 'IGNORE' ]
3516  );
3517  if ( !$dbw->affectedRows() ) {
3518  // Use locking reads to bypass any REPEATABLE-READ snapshot.
3519  $this->mId = $dbw->selectField(
3520  'user',
3521  'user_id',
3522  [ 'user_name' => $this->mName ],
3523  $fname,
3524  [ 'LOCK IN SHARE MODE' ]
3525  );
3526  $loaded = false;
3527  if ( $this->mId && $this->loadFromDatabase( IDBAccessObject::READ_LOCKING ) ) {
3528  $loaded = true;
3529  }
3530  if ( !$loaded ) {
3531  throw new MWException( $fname . ": hit a key conflict attempting " .
3532  "to insert user '{$this->mName}' row, but it was not present in select!" );
3533  }
3534  return Status::newFatal( 'userexists' );
3535  }
3536  $this->mId = $dbw->insertId();
3537 
3538  // Don't pass $this, since calling ::getId, ::getName might force ::load
3539  // and this user might not be ready for the yet.
3540  $this->mActorId = MediaWikiServices::getInstance()
3541  ->getActorNormalization()
3542  ->acquireActorId( new UserIdentityValue( $this->mId, $this->mName ), $dbw );
3543  return Status::newGood();
3544  } );
3545  if ( !$status->isGood() ) {
3546  return $status;
3547  }
3548 
3549  // Clear instance cache other than user table data and actor, which is already accurate
3550  $this->clearInstanceCache();
3551 
3552  MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
3553  return Status::newGood();
3554  }
3555 
3561  public function spreadAnyEditBlock() {
3562  if ( $this->isRegistered() && $this->getBlock() ) {
3563  return $this->spreadBlock();
3564  }
3565 
3566  return false;
3567  }
3568 
3574  protected function spreadBlock() {
3575  wfDebug( __METHOD__ . "()" );
3576  $this->load();
3577  if ( $this->mId == 0 ) {
3578  return false;
3579  }
3580 
3581  $userblock = DatabaseBlock::newFromTarget( $this->getName() );
3582  if ( !$userblock ) {
3583  return false;
3584  }
3585 
3586  return (bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
3587  }
3588 
3594  public function isBlockedFromCreateAccount() {
3595  $this->getBlockedStatus();
3596  if ( $this->mBlock && $this->mBlock->appliesToRight( 'createaccount' ) ) {
3597  return $this->mBlock;
3598  }
3599 
3600  # T15611: if the IP address the user is trying to create an account from is
3601  # blocked with createaccount disabled, prevent new account creation there even
3602  # when the user is logged in
3603  if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
3604  $this->mBlockedFromCreateAccount = DatabaseBlock::newFromTarget(
3605  null, $this->getRequest()->getIP()
3606  );
3607  }
3608  return $this->mBlockedFromCreateAccount instanceof AbstractBlock
3609  && $this->mBlockedFromCreateAccount->appliesToRight( 'createaccount' )
3610  ? $this->mBlockedFromCreateAccount
3611  : false;
3612  }
3613 
3618  public function isBlockedFromEmailuser() {
3619  $this->getBlockedStatus();
3620  return $this->mBlock && $this->mBlock->appliesToRight( 'sendemail' );
3621  }
3622 
3629  public function isBlockedFromUpload() {
3630  $this->getBlockedStatus();
3631  return $this->mBlock && $this->mBlock->appliesToRight( 'upload' );
3632  }
3633 
3638  public function isAllowedToCreateAccount() {
3639  return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
3640  }
3641 
3647  public function getUserPage() {
3648  return Title::makeTitle( NS_USER, $this->getName() );
3649  }
3650 
3656  public function getTalkPage() {
3657  $title = $this->getUserPage();
3658  return $title->getTalkPage();
3659  }
3660 
3666  public function isNewbie() {
3667  return !$this->isAllowed( 'autoconfirmed' );
3668  }
3669 
3682  public function getEditTokenObject( $salt = '', $request = null ) {
3683  if ( $this->isAnon() ) {
3684  return new LoggedOutEditToken();
3685  }
3686 
3687  if ( !$request ) {
3688  $request = $this->getRequest();
3689  }
3690  return $request->getSession()->getToken( $salt );
3691  }
3692 
3707  public function getEditToken( $salt = '', $request = null ) {
3708  return $this->getEditTokenObject( $salt, $request )->toString();
3709  }
3710 
3724  public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
3725  return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
3726  }
3727 
3739  public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
3740  wfDeprecated( __METHOD__, '1.37' );
3741  $val = substr( $val, 0, strspn( $val, '0123456789abcdef' ) ) . Token::SUFFIX;
3742  return $this->matchEditToken( $val, $salt, $request, $maxage );
3743  }
3744 
3752  public function sendConfirmationMail( $type = 'created' ) {
3753  global $wgLang;
3754  $expiration = null; // gets passed-by-ref and defined in next line.
3755  $token = $this->confirmationToken( $expiration );
3756  $url = $this->confirmationTokenUrl( $token );
3757  $invalidateURL = $this->invalidationTokenUrl( $token );
3758  $this->saveSettings();
3759 
3760  if ( $type == 'created' || $type === false ) {
3761  $message = 'confirmemail_body';
3762  $type = 'created';
3763  } elseif ( $type === true ) {
3764  $message = 'confirmemail_body_changed';
3765  $type = 'changed';
3766  } else {
3767  // Messages: confirmemail_body_changed, confirmemail_body_set
3768  $message = 'confirmemail_body_' . $type;
3769  }
3770 
3771  $mail = [
3772  'subject' => wfMessage( 'confirmemail_subject' )->text(),
3773  'body' => wfMessage( $message,
3774  $this->getRequest()->getIP(),
3775  $this->getName(),
3776  $url,
3777  $wgLang->userTimeAndDate( $expiration, $this ),
3778  $invalidateURL,
3779  $wgLang->userDate( $expiration, $this ),
3780  $wgLang->userTime( $expiration, $this ) )->text(),
3781  'from' => null,
3782  'replyTo' => null,
3783  ];
3784  $info = [
3785  'type' => $type,
3786  'ip' => $this->getRequest()->getIP(),
3787  'confirmURL' => $url,
3788  'invalidateURL' => $invalidateURL,
3789  'expiration' => $expiration
3790  ];
3791 
3792  $this->getHookRunner()->onUserSendConfirmationMail( $this, $mail, $info );
3793  return $this->sendMail( $mail['subject'], $mail['body'], $mail['from'], $mail['replyTo'] );
3794  }
3795 
3807  public function sendMail( $subject, $body, $from = null, $replyto = null ) {
3808  global $wgPasswordSender;
3809 
3810  if ( $from instanceof User ) {
3811  $sender = MailAddress::newFromUser( $from );
3812  } else {
3813  $sender = new MailAddress( $wgPasswordSender,
3814  wfMessage( 'emailsender' )->inContentLanguage()->text() );
3815  }
3816  $to = MailAddress::newFromUser( $this );
3817 
3818  return UserMailer::send( $to, $sender, $subject, $body, [
3819  'replyTo' => $replyto,
3820  ] );
3821  }
3822 
3833  protected function confirmationToken( &$expiration ) {
3835  $now = time();
3836  $expires = $now + $wgUserEmailConfirmationTokenExpiry;
3837  $expiration = wfTimestamp( TS_MW, $expires );
3838  $this->load();
3839  $token = MWCryptRand::generateHex( 32 );
3840  $hash = md5( $token );
3841  $this->mEmailToken = $hash;
3842  $this->mEmailTokenExpires = $expiration;
3843  return $token;
3844  }
3845 
3851  protected function confirmationTokenUrl( $token ) {
3852  return $this->getTokenUrl( 'ConfirmEmail', $token );
3853  }
3854 
3860  protected function invalidationTokenUrl( $token ) {
3861  return $this->getTokenUrl( 'InvalidateEmail', $token );
3862  }
3863 
3878  protected function getTokenUrl( $page, $token ) {
3879  // Hack to bypass localization of 'Special:'
3880  $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
3881  return $title->getCanonicalURL();
3882  }
3883 
3891  public function confirmEmail() {
3892  // Check if it's already confirmed, so we don't touch the database
3893  // and fire the ConfirmEmailComplete hook on redundant confirmations.
3894  if ( !$this->isEmailConfirmed() ) {
3896  $this->getHookRunner()->onConfirmEmailComplete( $this );
3897  }
3898  return true;
3899  }
3900 
3908  public function invalidateEmail() {
3909  $this->load();
3910  $this->mEmailToken = null;
3911  $this->mEmailTokenExpires = null;
3912  $this->setEmailAuthenticationTimestamp( null );
3913  $this->mEmail = '';
3914  $this->getHookRunner()->onInvalidateEmailComplete( $this );
3915  return true;
3916  }
3917 
3922  public function setEmailAuthenticationTimestamp( $timestamp ) {
3923  $this->load();
3924  $this->mEmailAuthenticated = $timestamp;
3925  $this->getHookRunner()->onUserSetEmailAuthenticationTimestamp(
3926  $this, $this->mEmailAuthenticated );
3927  }
3928 
3934  public function canSendEmail() {
3936  if ( !$wgEnableEmail || !$wgEnableUserEmail || !$this->isAllowed( 'sendemail' ) ) {
3937  return false;
3938  }
3939  $hookErr = $this->isEmailConfirmed();
3940  $this->getHookRunner()->onUserCanSendEmail( $this, $hookErr );
3941  return $hookErr;
3942  }
3943 
3949  public function canReceiveEmail() {
3950  return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
3951  }
3952 
3963  public function isEmailConfirmed(): bool {
3964  global $wgEmailAuthentication;
3965  $this->load();
3966  // Avoid PHP 7.1 warning of passing $this by reference
3967  $user = $this;
3968  $confirmed = true;
3969  if ( $this->getHookRunner()->onEmailConfirmed( $user, $confirmed ) ) {
3970  if ( $this->isAnon() ) {
3971  return false;
3972  }
3973  if ( !Sanitizer::validateEmail( $this->getEmail() ) ) {
3974  return false;
3975  }
3976  if ( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() ) {
3977  return false;
3978  }
3979  return true;
3980  }
3981 
3982  return $confirmed;
3983  }
3984 
3989  public function isEmailConfirmationPending() {
3990  global $wgEmailAuthentication;
3991  return $wgEmailAuthentication &&
3992  !$this->isEmailConfirmed() &&
3993  $this->mEmailToken &&
3994  $this->mEmailTokenExpires > wfTimestamp();
3995  }
3996 
4004  public function getRegistration() {
4005  if ( $this->isAnon() ) {
4006  return false;
4007  }
4008  $this->load();
4009  return $this->mRegistration;
4010  }
4011 
4020  public function getFirstEditTimestamp() {
4021  wfDeprecated( __METHOD__, '1.36' );
4022  return MediaWikiServices::getInstance()
4023  ->getUserEditTracker()
4024  ->getFirstEditTimestamp( $this );
4025  }
4026 
4036  public function getLatestEditTimestamp() {
4037  wfDeprecated( __METHOD__, '1.36' );
4038  return MediaWikiServices::getInstance()
4039  ->getUserEditTracker()
4040  ->getLatestEditTimestamp( $this );
4041  }
4042 
4052  public static function getGroupPermissions( $groups ) {
4053  return MediaWikiServices::getInstance()->getGroupPermissionsLookup()->getGroupPermissions( $groups );
4054  }
4055 
4065  public static function getGroupsWithPermission( $role ) {
4066  return MediaWikiServices::getInstance()->getGroupPermissionsLookup()->getGroupsWithPermission( $role );
4067  }
4068 
4084  public static function groupHasPermission( $group, $role ) {
4085  return MediaWikiServices::getInstance()->getGroupPermissionsLookup()
4086  ->groupHasPermission( $group, $role );
4087  }
4088 
4096  public static function getAllGroups() {
4097  return MediaWikiServices::getInstance()
4098  ->getUserGroupManager()
4099  ->listAllGroups();
4100  }
4101 
4106  public static function getImplicitGroups() {
4107  return MediaWikiServices::getInstance()
4108  ->getUserGroupManager()
4109  ->listAllImplicitGroups();
4110  }
4111 
4122  public static function changeableByGroup( $group ) {
4123  wfDeprecated( __METHOD__, '1.37' );
4124  return MediaWikiServices::getInstance()
4125  ->getUserGroupManager()
4126  ->getGroupsChangeableByGroup( $group );
4127  }
4128 
4137  public function changeableGroups() {
4138  wfDeprecated( __METHOD__, '1.37' );
4139  return MediaWikiServices::getInstance()
4140  ->getUserGroupManager()
4141  ->getGroupsChangeableBy( $this );
4142  }
4143 
4148  public function incEditCount() {
4149  MediaWikiServices::getInstance()->getUserEditTracker()->incrementUserEditCount( $this );
4150  }
4151 
4159  public static function getRightDescription( $right ) {
4160  $key = "right-$right";
4161  $msg = wfMessage( $key );
4162  return $msg->isDisabled() ? $right : $msg->text();
4163  }
4164 
4174  public static function getQueryInfo() {
4175  $ret = [
4176  'tables' => [ 'user', 'user_actor' => 'actor' ],
4177  'fields' => [
4178  'user_id',
4179  'user_name',
4180  'user_real_name',
4181  'user_email',
4182  'user_touched',
4183  'user_token',
4184  'user_email_authenticated',
4185  'user_email_token',
4186  'user_email_token_expires',
4187  'user_registration',
4188  'user_editcount',
4189  'user_actor.actor_id',
4190  ],
4191  'joins' => [
4192  'user_actor' => [ 'JOIN', 'user_actor.actor_user = user_id' ],
4193  ],
4194  ];
4195 
4196  return $ret;
4197  }
4198 
4206  public static function newFatalPermissionDeniedStatus( $permission ) {
4207  global $wgLang;
4208 
4209  $groups = [];
4210  foreach ( MediaWikiServices::getInstance()
4211  ->getGroupPermissionsLookup()
4212  ->getGroupsWithPermission( $permission ) as $group ) {
4213  $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(), 'wiki' );
4214  }
4215 
4216  if ( $groups ) {
4217  return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
4218  }
4219 
4220  return Status::newFatal( 'badaccess-group0' );
4221  }
4222 
4232  public function getInstanceForUpdate() {
4233  if ( !$this->getId() ) {
4234  return null; // anon
4235  }
4236 
4237  $user = self::newFromId( $this->getId() );
4238  if ( !$user->loadFromId( IDBAccessObject::READ_EXCLUSIVE ) ) {
4239  return null;
4240  }
4241 
4242  return $user;
4243  }
4244 
4252  public function equals( ?UserIdentity $user ): bool {
4253  if ( !$user ) {
4254  return false;
4255  }
4256  // XXX it's not clear whether central ID providers are supposed to obey this
4257  return $this->getName() === $user->getName();
4258  }
4259 
4265  public function isAllowUsertalk() {
4266  return $this->mAllowUsertalk;
4267  }
4268 
4274  public function getUser(): UserIdentity {
4275  return $this;
4276  }
4277 
4285  public function probablyCan( string $action, PageIdentity $target, PermissionStatus $status = null ): bool {
4286  return $this->getThisAsAuthority()->probablyCan( $action, $target, $status );
4287  }
4288 
4296  public function definitelyCan( string $action, PageIdentity $target, PermissionStatus $status = null ): bool {
4297  return $this->getThisAsAuthority()->definitelyCan( $action, $target, $status );
4298  }
4299 
4307  public function authorizeRead( string $action, PageIdentity $target, PermissionStatus $status = null
4308  ): bool {
4309  return $this->getThisAsAuthority()->authorizeRead( $action, $target, $status );
4310  }
4311 
4319  public function authorizeWrite( string $action, PageIdentity $target, PermissionStatus $status = null ): bool {
4320  return $this->getThisAsAuthority()->authorizeWrite( $action, $target, $status );
4321  }
4322 
4328  private function getThisAsAuthority(): Authority {
4329  if ( !$this->mThisAsAuthority ) {
4330  // TODO: For users that are not User::isGlobalSessionUser,
4331  // creating a UserAuthority here is incorrect, since it depends
4332  // on global WebRequest, but that is what we've used to do before Authority.
4333  // When PermissionManager is refactored into Authority, we need
4334  // to provide base implementation, based on just user groups/rights,
4335  // and use it here.
4336  $this->mThisAsAuthority = new UserAuthority(
4337  $this,
4338  MediaWikiServices::getInstance()->getPermissionManager()
4339  );
4340  }
4341  return $this->mThisAsAuthority;
4342  }
4343 
4348  private function isGlobalSessionUser(): bool {
4349  // The session user is set up towards the end of Setup.php. Until then,
4350  // assume it's a logged-out user.
4351  $sessionUser = RequestContext::getMain()->getUser();
4352  $globalUserName = $sessionUser->isSafeToLoad()
4353  ? $sessionUser->getName()
4354  : IPUtils::sanitizeIP( $sessionUser->getRequest()->getIP() );
4355 
4356  return $this->getName() === $globalUserName;
4357  }
4358 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1585
User\newFromConfirmationCode
static newFromConfirmationCode( $code, $flags=self::READ_NORMAL)
Factory method to fetch whichever user has a given email confirmation code.
Definition: User.php:729
$wgHiddenPrefs
$wgHiddenPrefs
An array of preferences to not show for the user.
Definition: DefaultSettings.php:5756
MediaWiki\User\UserIdentityValue
Value object representing a user's identity.
Definition: UserIdentityValue.php:35
Page\PageIdentity
Interface for objects (potentially) representing an editable wiki page.
Definition: PageIdentity.php:64
User\loadFromId
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
Definition: User.php:462
User\load
load( $flags=self::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition: User.php:374
MediaWiki\DAO\WikiAwareEntityTrait
trait WikiAwareEntityTrait
Definition: WikiAwareEntityTrait.php:32
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:52
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:643
Wikimedia\Rdbms\IDatabase\affectedRows
affectedRows()
Get the number of rows affected by the last write query.
User\confirmationTokenUrl
confirmationTokenUrl( $token)
Return a URL the user can use to confirm their email address.
Definition: User.php:3851
User\__set
__set( $name, $value)
Definition: User.php:313
MediaWiki\DAO\deprecateInvalidCrossWiki
deprecateInvalidCrossWiki( $wikiId, string $since)
Emits a deprecation warning $since version if $wikiId is not the same as this wiki.
Definition: WikiAwareEntityTrait.php:70
User\$mToken
string $mToken
Definition: User.php:170
User\$mBlockedby
string int $mBlockedby
Definition: User.php:211
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
ObjectCache\getLocalClusterInstance
static getLocalClusterInstance()
Get the main cluster-local cache object.
Definition: ObjectCache.php:272
User\isValidPassword
isValidPassword( $password)
Is the input a valid password for this user?
Definition: User.php:1096
User\useFilePatrol
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition: User.php:3054
$wgMaxArticleSize
$wgMaxArticleSize
Maximum article size in kibibytes.
Definition: DefaultSettings.php:2656
MWCryptHash\hmac
static hmac( $data, $key, $raw=true)
Generate an acceptably unstable one-way-hmac of some text making use of the best hash algorithm that ...
Definition: MWCryptHash.php:106
User\isAnon
isAnon()
Get whether the user is anonymous.
Definition: User.php:2978
User\$mBlockedFromCreateAccount
AbstractBlock bool $mBlockedFromCreateAccount
Definition: User.php:246
User\$mBlockreason
string $mBlockreason
TODO: This should be removed when User::BlockedFor and AbstractBlock::getReason are hard deprecated.
Definition: User.php:219
User\getTokenUrl
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
Definition: User.php:3878
User\isRegistered
isRegistered()
Get whether the user is registered.
Definition: User.php:2958
User\$mCacheVars
static string[] $mCacheVars
List of member variables which are saved to the shared cache (memcached).
Definition: User.php:130
User\resetTokenFromOption
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition: User.php:2662
User\isBlockedFrom
isBlockedFrom( $title, $fromReplica=false)
Check if user is blocked from editing a particular article.
Definition: User.php:1961
User\loadFromUserObject
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition: User.php:1445
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:186
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:4206
User\getEditTokenObject
getEditTokenObject( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
Definition: User.php:3682
User\isBot
isBot()
Definition: User.php:2986
User\newTouchedTimestamp
newTouchedTimestamp()
Generate a current or new-future timestamp to be stored in the user_touched field when we update thin...
Definition: User.php:2213
$wgExperiencedUserMemberSince
$wgExperiencedUserMemberSince
Number of days the user must exist before becoming "experienced".
Definition: DefaultSettings.php:5155
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:2910
User\spreadBlock
spreadBlock()
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition: User.php:3574
if
if(ini_get( 'mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition: Setup.php:88
User\incEditCount
incEditCount()
Schedule a deferred update to update the user's edit count.
Definition: User.php:4148
User\newFromSession
static newFromSession(WebRequest $request=null)
Create a new user object using data from session.
Definition: User.php:742
UserMailer\send
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...
Definition: UserMailer.php:115
User\getOptionKinds
getOptionKinds(IContextSource $context, $options=null)
Return an associative array mapping preferences keys to the kind of a preference they're used for.
Definition: User.php:2720
User\isEmailConfirmationPending
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
Definition: User.php:3989
User\getBlockId
getBlockId()
If user is blocked, return the ID for the block.
Definition: User.php:1992
User\getIntOption
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition: User.php:2600
true
return true
Definition: router.php:90
User\isWatched
isWatched(PageIdentity $title, $checkRights=self::CHECK_USER_RIGHTS)
Check the watched status of an article.
Definition: User.php:3084
User\getOptions
getOptions( $flags=0)
Get all user's options.
Definition: User.php:2570
User\$mTouched
string $mTouched
TS_MW timestamp from the DB.
Definition: User.php:166
User\__construct
__construct()
Lightweight constructor for an anonymous user.
Definition: User.php:269
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1692
User\getToken
getToken( $forceCreation=true)
Get the user's current token.
Definition: User.php:2369
User\setEmail
setEmail(string $str)
Set the user's e-mail address.
Definition: User.php:2452
User\spreadAnyEditBlock
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition: User.php:3561
User\removeWatch
removeWatch(PageIdentity $title, $checkRights=self::CHECK_USER_RIGHTS)
Stop watching an article.
Definition: User.php:3147
User\$mAllowUsertalk
bool $mAllowUsertalk
Definition: User.php:243
$wgEmailAuthentication
$wgEmailAuthentication
Require email authentication before sending mail to an email address.
Definition: DefaultSettings.php:1975
MediaWiki\Permissions\UserAuthority
Represents the authority of a given User.
Definition: UserAuthority.php:45
$wgEnableUserEmail
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
Definition: DefaultSettings.php:1863
User\$mHideName
bool $mHideName
Definition: User.php:230
User\getStubThreshold
getStubThreshold()
Get the user preferred stub threshold.
Definition: User.php:2797
Sanitizer\validateEmail
static validateEmail( $addr)
Does a string look like an e-mail address?
Definition: Sanitizer.php:1713
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1099
User\$mLocked
bool $mLocked
Definition: User.php:223
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:602
User\loadFromRow
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition: User.php:1338
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1182
User\setEmailAuthenticationTimestamp
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition: User.php:3922
User\$mEmail
string $mEmail
Definition: User.php:164
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:3647
User\getGroups
getGroups()
Get the list of explicit group memberships this user has.
Definition: User.php:2830
User\newFromIdentity
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition: User.php:679
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1056
User\useNPPatrol
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition: User.php:3042
DBAccessObjectUtils\getDBOptions
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
Definition: DBAccessObjectUtils.php:52
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:2761
User\isSafeToLoad
isSafeToLoad()
Test if it's safe to load this User object.
Definition: User.php:357
$success
$success
Definition: NoLocalSettings.php:42
User\isValidUserName
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:1008
Wikimedia\Rdbms\IDatabase\selectField
selectField( $table, $var, $cond='', $fname=__METHOD__, $options=[], $join_conds=[])
A SELECT wrapper which returns a single field from a single result row.
User\sendConfirmationMail
sendConfirmationMail( $type='created')
Generate a new e-mail confirmation token and send a confirmation/invalidation mail to the user's give...
Definition: User.php:3752
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4084
User\getEmailAuthenticationTimestamp
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition: User.php:2441
User\pingLimiter
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
Definition: User.php:1684
User\getActorId
getActorId( $dbwOrWikiId=self::LOCAL)
Get the user's actor ID.
Definition: User.php:2157
User\useRCPatrol
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition: User.php:3033
User\invalidateEmail
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
Definition: User.php:3908
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:764
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:8039
User\$mHash
string $mHash
Definition: User.php:213
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:8055
User\getBlock
getBlock( $freshness=self::READ_NORMAL, $disableIpBlockExemptChecking=false)
Get the block affecting the user, or null if the user is not blocked.
Definition: User.php:1936
$wgLang
$wgLang
Definition: Setup.php:807
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
User\isIPRange
isIPRange()
Is the user an IP range?
Definition: User.php:989
User\probablyCan
probablyCan(string $action, PageIdentity $target, PermissionStatus $status=null)
Definition: User.php:4285
User\getRights
getRights()
Get the permissions this user has.
Definition: User.php:2817
User\createNew
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition: User.php:3394
User\getRequest
getRequest()
Get the WebRequest object to use with this object.
Definition: User.php:3067
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
User\getWikiId
getWikiId()
Returns self::LOCAL to indicate the user is associated with the local wiki.
Definition: User.php:279
User\getAutomaticGroups
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:2880
User\INVALID_TOKEN
const INVALID_TOKEN
An invalid string value for the user_token field.
Definition: User.php:92
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1571
User\getInstanceForUpdate
getInstanceForUpdate()
Get a new instance of this user that was loaded from the master via a locking read.
Definition: User.php:4232
$dbr
$dbr
Definition: testCompression.php:54
$wgExperiencedUserEdits
$wgExperiencedUserEdits
Number of edits the user must have before becoming "experienced".
Definition: DefaultSettings.php:5149
User\newSystemUser
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition: User.php:805
Wikimedia\Rdbms\IDatabase\update
update( $table, $set, $conds, $fname=__METHOD__, $options=[])
Update all rows in a table that match a given condition.
User\addGroup
addGroup( $group, $expiry=null)
Add the user to the given group.
Definition: User.php:2929
MailAddress
Stores a single person's name and email address.
Definition: MailAddress.php:36
MWExceptionHandler\logException
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).
Definition: MWExceptionHandler.php:684
LoggedOutEditToken
Value object representing a logged-out user's edit token.
Definition: LoggedOutEditToken.php:37
User\matchEditToken
matchEditToken( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session.
Definition: User.php:3724
User\getEmail
getEmail()
Get the user's e-mail address.
Definition: User.php:2428
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:52
User\getTalkPage
getTalkPage()
Get this user's talk page title.
Definition: User.php:3656
User\equals
equals(?UserIdentity $user)
Checks if two user objects point to the same user.
Definition: User.php:4252
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:3487
Wikimedia\Rdbms\IDatabase\timestamp
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
User\invalidateCache
invalidateCache()
Immediately touch the user data cache for this account.
Definition: User.php:2258
TitleValue\castPageToLinkTarget
static castPageToLinkTarget(?PageReference $page)
Casts a PageReference to a LinkTarget.
Definition: TitleValue.php:136
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1028
MWException
MediaWiki exception.
Definition: MWException.php:29
User\invalidationTokenUrl
invalidationTokenUrl( $token)
Return a URL the user can use to invalidate their email address.
Definition: User.php:3860
User\isLocked
isLocked()
Check if user account is locked.
Definition: User.php:2050
User\$mDatePreference
string $mDatePreference
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition: User.php:204
$wgAuthenticationTokenVersion
string null $wgAuthenticationTokenVersion
Versioning for authentication tokens.
Definition: DefaultSettings.php:5794
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:996
User\authorizeWrite
authorizeWrite(string $action, PageIdentity $target, PermissionStatus $status=null)
Definition: User.php:4319
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
User\checkPasswordValidity
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition: User.php:1122
User\confirmEmail
confirmEmail()
Mark the e-mail address confirmed.
Definition: User.php:3891
User\setItemLoaded
setItemLoaded( $item)
Set that an item has been loaded.
Definition: User.php:1249
UserGroupMembership\getLink
static getLink( $ugm, IContextSource $context, $format, $userName=null)
Gets a link for a user group, possibly including the expiry date if relevant.
Definition: UserGroupMembership.php:121
User\blockedFor
blockedFor()
If user is blocked, return the specified reason for the block.
Definition: User.php:1983
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2226
$wgFullyInitialised
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) if(! $wgCommandLineMode) $wgFullyInitialised
Definition: Setup.php:861
MailAddress\newFromUser
static newFromUser(UserEmailContact $user)
Create a new MailAddress object for the given user.
Definition: MailAddress.php:72
User\isAllowedToCreateAccount
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition: User.php:3638
User\logout
logout()
Log this user out.
Definition: User.php:3241
User\confirmationToken
confirmationToken(&$expiration)
Generate, store, and return a new e-mail confirmation code.
Definition: User.php:3833
User\getCacheKey
getCacheKey(WANObjectCache $cache)
Definition: User.php:503
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:1708
User\getImplicitGroups
static getImplicitGroups()
Definition: User.php:4106
User\setRealName
setRealName(string $str)
Set the user's real name.
Definition: User.php:2534
User\isHidden
isHidden()
Check if user account is hidden.
Definition: User.php:2065
User\definitelyCan
definitelyCan(string $action, PageIdentity $target, PermissionStatus $status=null)
Definition: User.php:4296
User\isBlockedFromEmailuser
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition: User.php:3618
User\setEmailWithConfirmation
setEmailWithConfirmation(string $str)
Set the user's e-mail address and a confirmation mail if needed.
Definition: User.php:2469
User\$mBlock
AbstractBlock null $mBlock
Definition: User.php:240
User\loadDefaults
loadDefaults( $name=false, $actorId=null)
Set cached properties to default.
Definition: User.php:1201
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:974
User\validateCache
validateCache( $timestamp)
Validate the cache for this account.
Definition: User.php:2290
User\getEffectiveGroups
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:2862
User\isPingLimitable
isPingLimitable()
Is this user subject to rate limiting?
Definition: User.php:1657
User\removeGroup
removeGroup( $group)
Remove the user from the given group.
Definition: User.php:2944
User\canReceiveEmail
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition: User.php:3949
User\isNewbie
isNewbie()
Determine whether the user is a newbie.
Definition: User.php:3666
User\touch
touch()
Update the "touched" timestamp for the user.
Definition: User.php:2275
MediaWiki\User\UserIdentity\getName
getName()
$title
$title
Definition: testCompression.php:38
User\clearInstanceCache
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition: User.php:1541
$wgEnableEmail
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
Definition: DefaultSettings.php:1857
User\CHECK_USER_RIGHTS
const CHECK_USER_RIGHTS
Definition: User.php:110
User\TOKEN_LENGTH
const TOKEN_LENGTH
Number of characters required for the user_token field.
Definition: User.php:87
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:680
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1721
User\$mQuickTouched
string $mQuickTouched
TS_MW timestamp from cache.
Definition: User.php:168
User\newFromAnyId
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition: User.php:708
User\getId
getId( $wikiId=self::LOCAL)
Get the user's ID.
Definition: User.php:2078
User\setName
setName( $str)
Set the user name.
Definition: User.php:2139
User\$mRealName
string $mRealName
Definition: User.php:161
User\resetOptions
resetOptions( $resetKinds=[ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused'], IContextSource $context=null)
Reset certain (or all) options to the site defaults.
Definition: User.php:2743
UserArray\newFromIDs
static newFromIDs( $ids)
Definition: UserArray.php:52
User\getBlockedStatus
getBlockedStatus( $fromReplica=true, $disableIpBlockExemptChecking=false)
Get blocking information.
Definition: User.php:1603
UserPasswordPolicy
Check if a user's password complies with any password policies that apply to that user,...
Definition: UserPasswordPolicy.php:31
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:8066
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:894
User\__get
& __get( $name)
Definition: User.php:290
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:3289
User\isAllowedAll
isAllowedAll(... $permissions)
Checks whether this authority has any of the given permissions in general.
Definition: User.php:3021
User\getFirstEditTimestamp
getFirstEditTimestamp()
Get the timestamp of the first edit.
Definition: User.php:4020
User\authorizeRead
authorizeRead(string $action, PageIdentity $target, PermissionStatus $status=null)
Definition: User.php:4307
User\GETOPTIONS_EXCLUDE_DEFAULTS
const GETOPTIONS_EXCLUDE_DEFAULTS
Exclude user options that are set to their default value.
Definition: User.php:105
MediaWiki\Block\Block
Represents a block that may prevent users from performing specific operations.
Definition: Block.php:37
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
User\getFormerGroups
getFormerGroups()
Returns the groups the user has belonged to.
Definition: User.php:2899
MediaWiki\DAO\WikiAwareEntity\assertWiki
assertWiki( $wikiId)
Throws if $wikiId is different from the return value of getWikiId().
$s
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
Definition: mergeMessageFileList.php:206
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:912
DBAccessObjectUtils\hasFlags
static hasFlags( $bitfield, $flags)
Definition: DBAccessObjectUtils.php:35
User\isAllowedAny
isAllowedAny(... $permissions)
Checks whether this authority has any of the given permissions in general.
Definition: User.php:3017
User\loadFromSession
loadFromSession()
Load user data from the session.
Definition: User.php:1260
$wgRateLimits
$wgRateLimits
Simple rate limiter options to brake edit floods.
Definition: DefaultSettings.php:6552
User\isBlockedGlobally
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2005
$wgForceHTTPS
bool $wgForceHTTPS
If this is true, when an insecure HTTP request is received, always redirect to HTTPS.
Definition: DefaultSettings.php:165
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:2551
User\idForName
idForName( $flags=self::READ_NORMAL)
If only this user's username is known, and it exists, return the user ID.
Definition: User.php:3365
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
User\getTouched
getTouched()
Get the user touched timestamp.
Definition: User.php:2302
User\$mId
int $mId
Cache variables.
Definition: User.php:151
User\isSystemUser
isSystemUser()
Get whether the user is a system user.
Definition: User.php:3007
User\getGlobalBlock
getGlobalBlock( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2019
User\__toString
__toString()
Definition: User.php:286
MediaWiki\Block\SystemBlock
System blocks are temporary blocks that are created on enforcement (e.g.
Definition: SystemBlock.php:35
MediaWiki\Session\SessionManager
This serves as the entry point to the MediaWiki session handling system.
Definition: SessionManager.php:83
User\checkAndSetTouched
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
Definition: User.php:1503
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:128
User\getRealName
getRealName()
Get the user's real name.
Definition: User.php:2522
User\setCookies
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
Definition: User.php:3205
User\getGroupPermissions
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
Definition: User.php:4052
User\changeableGroups
changeableGroups()
Returns an array of groups that this user can add and remove.
Definition: User.php:4137
User\VERSION
const VERSION
Version number to tag cached versions of serialized User objects.
Definition: User.php:98
MediaWiki\Mail\UserEmailContact
Definition: UserEmailContact.php:11
User\matchEditTokenNoSuffix
matchEditTokenNoSuffix( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session, ignoring the suffix.
Definition: User.php:3739
User\getAllGroups
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4096
NS_USER
const NS_USER
Definition: Defines.php:66
MWCryptRand\generateHex
static generateHex( $chars)
Generate a run of cryptographically random data and return it in hexadecimal string format.
Definition: MWCryptRand.php:36
User\isBlockedFromUpload
isBlockedFromUpload()
Get whether the user is blocked from using Special:Upload.
Definition: User.php:3629
User\setActorId
setActorId(int $actorId)
Sets the actor id.
Definition: User.php:2194
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:484
User\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition: User.php:4174
MediaWiki\User\UserOptionsLookup
Provides access to user options.
Definition: UserOptionsLookup.php:29
User\blockedBy
blockedBy()
If user is blocked, return the name of the user who placed the block.
Definition: User.php:1972
User\$mLoadedItems
array bool $mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition: User.php:185
$wgLearnerEdits
$wgLearnerEdits
The following variables define 3 user experience levels:
Definition: DefaultSettings.php:5137
User\getDBTouched
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition: User.php:2324
Wikimedia\Rdbms\IDatabase\insert
insert( $table, $rows, $fname=__METHOD__, $options=[])
Insert the given row(s) into a table.
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:96
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:58
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:42
MediaWiki\Permissions\PermissionStatus
A StatusValue for permission errors.
Definition: PermissionStatus.php:35
User\changeableByGroup
static changeableByGroup( $group)
Returns an array of the groups that a particular group can add/remove.
Definition: User.php:4122
$watchlistManager
WatchlistManager $watchlistManager
Definition: ApiWatchlistTrait.php:30
User\setId
setId( $v)
Set the user and reload all fields according to a given ID.
Definition: User.php:2102
User\getExperienceLevel
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition: User.php:3162
$wgUserEmailConfirmationTokenExpiry
$wgUserEmailConfirmationTokenExpiry
The time, in seconds, when an email confirmation email expires.
Definition: DefaultSettings.php:1907
User\getRegistration
getRegistration()
Get the timestamp of account creation.
Definition: User.php:4004
User\isAllowed
isAllowed(string $permission)
Checks whether this authority has the given permission in general.
Definition: User.php:3025
PasswordFactory\newInvalidPassword
static newInvalidPassword()
Create an InvalidPassword.
Definition: PasswordFactory.php:242
MediaWiki\Block\AbstractBlock\appliesToRight
appliesToRight( $right)
Determine whether the block prevents a given right.
Definition: AbstractBlock.php:268
User\getRightDescription
static getRightDescription( $right)
Get the description of a given right.
Definition: User.php:4159
User\changeAuthenticationData
changeAuthenticationData(array $data)
Changes credentials of the user.
Definition: User.php:2342
$cache
$cache
Definition: mcc.php:33
$wgRateLimitsExcludedIPs
$wgRateLimitsExcludedIPs
Array of IPs / CIDR ranges which should be excluded from rate limits.
Definition: DefaultSettings.php:6634
User\isLoggedIn
isLoggedIn()
Get whether the user is registered.
Definition: User.php:2968
Wikimedia\Rdbms\DBExpectedError
Base class for the more common types of database errors.
Definition: DBExpectedError.php:34
User\$mActorId
int null $mActorId
Switched from protected to public for use in UserFactory.
Definition: User.php:159
User\getTitleKey
getTitleKey()
Get the user's name escaped by underscores.
Definition: User.php:2203
User\purge
static purge( $dbDomain, $userId)
Definition: User.php:492
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2412
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:933
MediaWiki\User\UserNameUtils
UserNameUtils service.
Definition: UserNameUtils.php:42
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:949
User\$mEmailTokenExpires
string $mEmailTokenExpires
Definition: User.php:176
User\findUsersByGroup
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition: User.php:1042
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1168
User\isEmailConfirmed
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition: User.php:3963
User\$mGlobalBlock
AbstractBlock $mGlobalBlock
Definition: User.php:221
User\isAllowUsertalk
isAllowUsertalk()
Checks if usertalk is allowed.
Definition: User.php:4265
UserCache\singleton
static singleton()
Definition: UserCache.php:48
User\clearSharedCache
clearSharedCache( $mode='refresh')
Clear user data from memcached.
Definition: User.php:2232
User\isBlocked
isBlocked( $fromReplica=true)
Check if user is blocked.
Definition: User.php:1920
User\newFromActorId
static newFromActorId( $id)
Static factory method for creation from a given actor ID.
Definition: User.php:660
User\$mThisAsAuthority
Authority null $mThisAsAuthority
lazy-initialized Authority of this user
Definition: User.php:252
$keys
$keys
Definition: testCompression.php:72
User\loadFromCache
loadFromCache()
Load user data from shared cache, given mId has already been set.
Definition: User.php:526
$wgLearnerMemberSince
$wgLearnerMemberSince
Number of days the user must exist before becoming a learner.
Definition: DefaultSettings.php:5143
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1468
User\sendMail
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition: User.php:3807
User\getTokenFromOption
getTokenFromOption( $oname)
Get a token stored in the preferences (like the watchlist one), resetting it if it's empty (and savin...
Definition: User.php:2634
User\$mEmailToken
string $mEmailToken
Definition: User.php:174
Wikimedia\Rdbms\IDatabase\timestampOrNull
timestampOrNull( $ts=null)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
User\loadFromDatabase
loadFromDatabase( $flags=self::READ_LATEST)
Load user data from the database.
Definition: User.php:1286
User\$mName
string $mName
Definition: User.php:153
User\$mRegistration
string $mRegistration
Definition: User.php:178
MediaWiki\Block\AbstractBlock
Definition: AbstractBlock.php:37
$wgPasswordPolicy
$wgPasswordPolicy
Password policy for the wiki.
Definition: DefaultSettings.php:5283
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:115
User\$mRequest
WebRequest $mRequest
Definition: User.php:233
MediaWiki\Session\Token
Value object representing a CSRF token.
Definition: Token.php:32
CentralIdLookup\AUDIENCE_RAW
const AUDIENCE_RAW
Definition: CentralIdLookup.php:38
User\getBoolOption
getBoolOption( $oname)
Get the user's current setting for a given option, as a boolean value.
Definition: User.php:2585
Wikimedia\Rdbms\IDatabase\insertId
insertId()
Get the inserted value of an auto-increment row.
User\isUsableName
static isUsableName( $name)
Usernames which fail to pass this function will be blocked from user login and new account registrati...
Definition: User.php:1027
User\makeUpdateConditions
makeUpdateConditions(IDatabase $db, array $conditions)
Builds update conditions.
Definition: User.php:1484
$wgPasswordSender
$wgPasswordSender
Sender email address for e-mail notifications.
Definition: DefaultSettings.php:1843
User\__sleep
__sleep()
Definition: User.php:334
User\isBlockedFromCreateAccount
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
Definition: User.php:3594
User\getEditToken
getEditToken( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
Definition: User.php:3707
User\requiresHTTPS
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition: User.php:2781
User\isItemLoaded
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition: User.php:1237
User\addWatch
addWatch(PageIdentity $title, $checkRights=self::CHECK_USER_RIGHTS, ?string $expiry=null)
Watch an article.
Definition: User.php:3124
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:68
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:2618
User\$queryFlagsUsed
int $queryFlagsUsed
User::READ_* constant bitfield used to load data.
Definition: User.php:249
User\whoIsReal
static whoIsReal( $id)
Get the real name of a user given their user ID.
Definition: User.php:922
ExternalUserNames\isExternal
static isExternal( $username)
Tells whether the username is external or not.
Definition: ExternalUserNames.php:149
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2111
$wgSecureLogin
$wgSecureLogin
This is to let user authenticate using https when they come from http.
Definition: DefaultSettings.php:5782
User\doLogout
doLogout()
Clear the user's session, and reset the instance cache.
Definition: User.php:3253
MediaWiki\User\UserFactory
Creates User objects.
Definition: UserFactory.php:41
User\getGroupMemberships
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
Definition: User.php:2845
User\canSendEmail
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition: User.php:3934
User\insertNewUser
static insertNewUser(callable $insertActor, $name, $params=[])
See ::createNew.
Definition: User.php:3407
User\isTempWatched
isTempWatched(PageIdentity $title, $checkRights=self::CHECK_USER_RIGHTS)
Check if the article is temporarily watched.
Definition: User.php:3105
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:1085
User\isGlobalSessionUser
isGlobalSessionUser()
Check whether this is the global session user.
Definition: User.php:4348
User\getUser
getUser()
Definition: User.php:4274
User\getMutableCacheKeys
getMutableCacheKeys(WANObjectCache $cache)
Definition: User.php:514
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:38
User\getLatestEditTimestamp
getLatestEditTimestamp()
Get the timestamp of the latest edit.
Definition: User.php:4036
User\MAINTENANCE_SCRIPT_USER
const MAINTENANCE_SCRIPT_USER
Username used for various maintenance scripts.
Definition: User.php:121
User\$mEmailAuthenticated
string $mEmailAuthenticated
Definition: User.php:172
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4065
User\$mFrom
string $mFrom
Initialization data source if mLoadedItems!==true.
Definition: User.php:198
User\listOptionKinds
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
Definition: User.php:2698
User\getThisAsAuthority
getThisAsAuthority()
Returns the Authority of this User if it's the main request context user.
Definition: User.php:4328
$type
$type
Definition: testCompression.php:52