MediaWiki  1.29.1
User.php
Go to the documentation of this file.
1 <?php
29 use Wikimedia\ScopedCallback;
32 
38 define( 'EDIT_TOKEN_SUFFIX', Token::SUFFIX );
39 
50 class User implements IDBAccessObject {
54  const TOKEN_LENGTH = 32;
55 
59  const INVALID_TOKEN = '*** INVALID ***';
60 
67 
71  const VERSION = 11;
72 
78 
82  const CHECK_USER_RIGHTS = true;
83 
87  const IGNORE_USER_RIGHTS = false;
88 
95  protected static $mCacheVars = [
96  // user table
97  'mId',
98  'mName',
99  'mRealName',
100  'mEmail',
101  'mTouched',
102  'mToken',
103  'mEmailAuthenticated',
104  'mEmailToken',
105  'mEmailTokenExpires',
106  'mRegistration',
107  'mEditCount',
108  // user_groups table
109  'mGroupMemberships',
110  // user_properties table
111  'mOptionOverrides',
112  ];
113 
120  protected static $mCoreRights = [
121  'apihighlimits',
122  'applychangetags',
123  'autoconfirmed',
124  'autocreateaccount',
125  'autopatrol',
126  'bigdelete',
127  'block',
128  'blockemail',
129  'bot',
130  'browsearchive',
131  'changetags',
132  'createaccount',
133  'createpage',
134  'createtalk',
135  'delete',
136  'deletechangetags',
137  'deletedhistory',
138  'deletedtext',
139  'deletelogentry',
140  'deleterevision',
141  'edit',
142  'editcontentmodel',
143  'editinterface',
144  'editprotected',
145  'editmyoptions',
146  'editmyprivateinfo',
147  'editmyusercss',
148  'editmyuserjs',
149  'editmywatchlist',
150  'editsemiprotected',
151  'editusercss',
152  'edituserjs',
153  'hideuser',
154  'import',
155  'importupload',
156  'ipblock-exempt',
157  'managechangetags',
158  'markbotedits',
159  'mergehistory',
160  'minoredit',
161  'move',
162  'movefile',
163  'move-categorypages',
164  'move-rootuserpages',
165  'move-subpages',
166  'nominornewtalk',
167  'noratelimit',
168  'override-export-depth',
169  'pagelang',
170  'patrol',
171  'patrolmarks',
172  'protect',
173  'purge',
174  'read',
175  'reupload',
176  'reupload-own',
177  'reupload-shared',
178  'rollback',
179  'sendemail',
180  'siteadmin',
181  'suppressionlog',
182  'suppressredirect',
183  'suppressrevision',
184  'unblockself',
185  'undelete',
186  'unwatchedpages',
187  'upload',
188  'upload_by_url',
189  'userrights',
190  'userrights-interwiki',
191  'viewmyprivateinfo',
192  'viewmywatchlist',
193  'viewsuppressed',
194  'writeapi',
195  ];
196 
200  protected static $mAllRights = false;
201 
203  // @{
205  public $mId;
207  public $mName;
209  public $mRealName;
210 
212  public $mEmail;
214  public $mTouched;
216  protected $mQuickTouched;
218  protected $mToken;
222  protected $mEmailToken;
226  protected $mRegistration;
228  protected $mEditCount;
233  private $mGroups;
237  protected $mOptionOverrides;
238  // @}
239 
243  // @{
245 
249  protected $mLoadedItems = [];
250  // @}
251 
261  public $mFrom;
262 
266  protected $mNewtalk;
268  protected $mDatePreference;
270  public $mBlockedby;
272  protected $mHash;
274  public $mRights;
276  protected $mBlockreason;
278  protected $mEffectiveGroups;
280  protected $mImplicitGroups;
282  protected $mFormerGroups;
284  protected $mGlobalBlock;
286  protected $mLocked;
288  public $mHideName;
290  public $mOptions;
291 
293  private $mRequest;
294 
296  public $mBlock;
297 
299  protected $mAllowUsertalk;
300 
302  private $mBlockedFromCreateAccount = false;
303 
305  protected $queryFlagsUsed = self::READ_NORMAL;
306 
311  public $blockTrigger = false;
312 
313  public static $idCacheByName = [];
314 
325  public function __construct() {
326  $this->clearInstanceCache( 'defaults' );
327  }
328 
332  public function __toString() {
333  return (string)$this->getName();
334  }
335 
350  public function isSafeToLoad() {
352 
353  // The user is safe to load if:
354  // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data)
355  // * mLoadedItems === true (already loaded)
356  // * mFrom !== 'session' (sessions not involved at all)
357 
358  return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) ||
359  $this->mLoadedItems === true || $this->mFrom !== 'session';
360  }
361 
367  public function load( $flags = self::READ_NORMAL ) {
369 
370  if ( $this->mLoadedItems === true ) {
371  return;
372  }
373 
374  // Set it now to avoid infinite recursion in accessors
375  $oldLoadedItems = $this->mLoadedItems;
376  $this->mLoadedItems = true;
377  $this->queryFlagsUsed = $flags;
378 
379  // If this is called too early, things are likely to break.
380  if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
382  ->warning( 'User::loadFromSession called before the end of Setup.php', [
383  'exception' => new Exception( 'User::loadFromSession called before the end of Setup.php' ),
384  ] );
385  $this->loadDefaults();
386  $this->mLoadedItems = $oldLoadedItems;
387  return;
388  }
389 
390  switch ( $this->mFrom ) {
391  case 'defaults':
392  $this->loadDefaults();
393  break;
394  case 'name':
395  // Make sure this thread sees its own changes
396  if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) {
397  $flags |= self::READ_LATEST;
398  $this->queryFlagsUsed = $flags;
399  }
400 
401  $this->mId = self::idFromName( $this->mName, $flags );
402  if ( !$this->mId ) {
403  // Nonexistent user placeholder object
404  $this->loadDefaults( $this->mName );
405  } else {
406  $this->loadFromId( $flags );
407  }
408  break;
409  case 'id':
410  $this->loadFromId( $flags );
411  break;
412  case 'session':
413  if ( !$this->loadFromSession() ) {
414  // Loading from session failed. Load defaults.
415  $this->loadDefaults();
416  }
417  Hooks::run( 'UserLoadAfterLoadFromSession', [ $this ] );
418  break;
419  default:
420  throw new UnexpectedValueException(
421  "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
422  }
423  }
424 
430  public function loadFromId( $flags = self::READ_NORMAL ) {
431  if ( $this->mId == 0 ) {
432  // Anonymous users are not in the database (don't need cache)
433  $this->loadDefaults();
434  return false;
435  }
436 
437  // Try cache (unless this needs data from the master DB).
438  // NOTE: if this thread called saveSettings(), the cache was cleared.
439  $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
440  if ( $latest ) {
441  if ( !$this->loadFromDatabase( $flags ) ) {
442  // Can't load from ID
443  return false;
444  }
445  } else {
446  $this->loadFromCache();
447  }
448 
449  $this->mLoadedItems = true;
450  $this->queryFlagsUsed = $flags;
451 
452  return true;
453  }
454 
460  public static function purge( $wikiId, $userId ) {
462  $key = $cache->makeGlobalKey( 'user', 'id', $wikiId, $userId );
463  $cache->delete( $key );
464  }
465 
471  protected function getCacheKey( WANObjectCache $cache ) {
472  return $cache->makeGlobalKey( 'user', 'id', wfWikiID(), $this->mId );
473  }
474 
481  $id = $this->getId();
482 
483  return $id ? [ $this->getCacheKey( $cache ) ] : [];
484  }
485 
492  protected function loadFromCache() {
494  $data = $cache->getWithSetCallback(
495  $this->getCacheKey( $cache ),
496  $cache::TTL_HOUR,
497  function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
498  $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
499  wfDebug( "User: cache miss for user {$this->mId}\n" );
500 
501  $this->loadFromDatabase( self::READ_NORMAL );
502  $this->loadGroups();
503  $this->loadOptions();
504 
505  $data = [];
506  foreach ( self::$mCacheVars as $name ) {
507  $data[$name] = $this->$name;
508  }
509 
510  $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
511 
512  // if a user group membership is about to expire, the cache needs to
513  // expire at that time (T163691)
514  foreach ( $this->mGroupMemberships as $ugm ) {
515  if ( $ugm->getExpiry() ) {
516  $secondsUntilExpiry = wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
517  if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
518  $ttl = $secondsUntilExpiry;
519  }
520  }
521  }
522 
523  return $data;
524 
525  },
526  [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ]
527  );
528 
529  // Restore from cache
530  foreach ( self::$mCacheVars as $name ) {
531  $this->$name = $data[$name];
532  }
533 
534  return true;
535  }
536 
538  // @{
539 
556  public static function newFromName( $name, $validate = 'valid' ) {
557  if ( $validate === true ) {
558  $validate = 'valid';
559  }
560  $name = self::getCanonicalName( $name, $validate );
561  if ( $name === false ) {
562  return false;
563  } else {
564  // Create unloaded user object
565  $u = new User;
566  $u->mName = $name;
567  $u->mFrom = 'name';
568  $u->setItemLoaded( 'name' );
569  return $u;
570  }
571  }
572 
579  public static function newFromId( $id ) {
580  $u = new User;
581  $u->mId = $id;
582  $u->mFrom = 'id';
583  $u->setItemLoaded( 'id' );
584  return $u;
585  }
586 
598  public static function newFromConfirmationCode( $code, $flags = 0 ) {
599  $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
600  ? wfGetDB( DB_MASTER )
601  : wfGetDB( DB_REPLICA );
602 
603  $id = $db->selectField(
604  'user',
605  'user_id',
606  [
607  'user_email_token' => md5( $code ),
608  'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
609  ]
610  );
611 
612  return $id ? User::newFromId( $id ) : null;
613  }
614 
622  public static function newFromSession( WebRequest $request = null ) {
623  $user = new User;
624  $user->mFrom = 'session';
625  $user->mRequest = $request;
626  return $user;
627  }
628 
643  public static function newFromRow( $row, $data = null ) {
644  $user = new User;
645  $user->loadFromRow( $row, $data );
646  return $user;
647  }
648 
684  public static function newSystemUser( $name, $options = [] ) {
685  $options += [
686  'validate' => 'valid',
687  'create' => true,
688  'steal' => false,
689  ];
690 
691  $name = self::getCanonicalName( $name, $options['validate'] );
692  if ( $name === false ) {
693  return null;
694  }
695 
696  $fields = self::selectFields();
697 
698  $dbw = wfGetDB( DB_MASTER );
699  $row = $dbw->selectRow(
700  'user',
701  $fields,
702  [ 'user_name' => $name ],
703  __METHOD__
704  );
705  if ( !$row ) {
706  // No user. Create it?
707  return $options['create'] ? self::createNew( $name, [ 'token' => self::INVALID_TOKEN ] ) : null;
708  }
709  $user = self::newFromRow( $row );
710 
711  // A user is considered to exist as a non-system user if it can
712  // authenticate, or has an email set, or has a non-invalid token.
713  if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN ||
714  AuthManager::singleton()->userCanAuthenticate( $name )
715  ) {
716  // User exists. Steal it?
717  if ( !$options['steal'] ) {
718  return null;
719  }
720 
721  AuthManager::singleton()->revokeAccessForUser( $name );
722 
723  $user->invalidateEmail();
724  $user->mToken = self::INVALID_TOKEN;
725  $user->saveSettings();
726  SessionManager::singleton()->preventSessionsForUser( $user->getName() );
727  }
728 
729  return $user;
730  }
731 
732  // @}
733 
739  public static function whoIs( $id ) {
740  return UserCache::singleton()->getProp( $id, 'name' );
741  }
742 
749  public static function whoIsReal( $id ) {
750  return UserCache::singleton()->getProp( $id, 'real_name' );
751  }
752 
759  public static function idFromName( $name, $flags = self::READ_NORMAL ) {
761  if ( is_null( $nt ) ) {
762  // Illegal name
763  return null;
764  }
765 
766  if ( !( $flags & self::READ_LATEST ) && isset( self::$idCacheByName[$name] ) ) {
767  return self::$idCacheByName[$name];
768  }
769 
771  $db = wfGetDB( $index );
772 
773  $s = $db->selectRow(
774  'user',
775  [ 'user_id' ],
776  [ 'user_name' => $nt->getText() ],
777  __METHOD__,
778  $options
779  );
780 
781  if ( $s === false ) {
782  $result = null;
783  } else {
784  $result = $s->user_id;
785  }
786 
787  self::$idCacheByName[$name] = $result;
788 
789  if ( count( self::$idCacheByName ) > 1000 ) {
790  self::$idCacheByName = [];
791  }
792 
793  return $result;
794  }
795 
799  public static function resetIdByNameCache() {
800  self::$idCacheByName = [];
801  }
802 
819  public static function isIP( $name ) {
820  return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name )
821  || IP::isIPv6( $name );
822  }
823 
835  public static function isValidUserName( $name ) {
836  global $wgContLang, $wgMaxNameChars;
837 
838  if ( $name == ''
839  || User::isIP( $name )
840  || strpos( $name, '/' ) !== false
841  || strlen( $name ) > $wgMaxNameChars
842  || $name != $wgContLang->ucfirst( $name )
843  ) {
844  return false;
845  }
846 
847  // Ensure that the name can't be misresolved as a different title,
848  // such as with extra namespace keys at the start.
849  $parsed = Title::newFromText( $name );
850  if ( is_null( $parsed )
851  || $parsed->getNamespace()
852  || strcmp( $name, $parsed->getPrefixedText() ) ) {
853  return false;
854  }
855 
856  // Check an additional blacklist of troublemaker characters.
857  // Should these be merged into the title char list?
858  $unicodeBlacklist = '/[' .
859  '\x{0080}-\x{009f}' . # iso-8859-1 control chars
860  '\x{00a0}' . # non-breaking space
861  '\x{2000}-\x{200f}' . # various whitespace
862  '\x{2028}-\x{202f}' . # breaks and control chars
863  '\x{3000}' . # ideographic space
864  '\x{e000}-\x{f8ff}' . # private use
865  ']/u';
866  if ( preg_match( $unicodeBlacklist, $name ) ) {
867  return false;
868  }
869 
870  return true;
871  }
872 
884  public static function isUsableName( $name ) {
885  global $wgReservedUsernames;
886  // Must be a valid username, obviously ;)
887  if ( !self::isValidUserName( $name ) ) {
888  return false;
889  }
890 
891  static $reservedUsernames = false;
892  if ( !$reservedUsernames ) {
893  $reservedUsernames = $wgReservedUsernames;
894  Hooks::run( 'UserGetReservedNames', [ &$reservedUsernames ] );
895  }
896 
897  // Certain names may be reserved for batch processes.
898  foreach ( $reservedUsernames as $reserved ) {
899  if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
900  $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
901  }
902  if ( $reserved == $name ) {
903  return false;
904  }
905  }
906  return true;
907  }
908 
919  public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
920  if ( $groups === [] ) {
921  return UserArrayFromResult::newFromIDs( [] );
922  }
923 
924  $groups = array_unique( (array)$groups );
925  $limit = min( 5000, $limit );
926 
927  $conds = [ 'ug_group' => $groups ];
928  if ( $after !== null ) {
929  $conds[] = 'ug_user > ' . (int)$after;
930  }
931 
932  $dbr = wfGetDB( DB_REPLICA );
933  $ids = $dbr->selectFieldValues(
934  'user_groups',
935  'ug_user',
936  $conds,
937  __METHOD__,
938  [
939  'DISTINCT' => true,
940  'ORDER BY' => 'ug_user',
941  'LIMIT' => $limit,
942  ]
943  ) ?: [];
944  return UserArray::newFromIDs( $ids );
945  }
946 
959  public static function isCreatableName( $name ) {
960  global $wgInvalidUsernameCharacters;
961 
962  // Ensure that the username isn't longer than 235 bytes, so that
963  // (at least for the builtin skins) user javascript and css files
964  // will work. (T25080)
965  if ( strlen( $name ) > 235 ) {
966  wfDebugLog( 'username', __METHOD__ .
967  ": '$name' invalid due to length" );
968  return false;
969  }
970 
971  // Preg yells if you try to give it an empty string
972  if ( $wgInvalidUsernameCharacters !== '' ) {
973  if ( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) {
974  wfDebugLog( 'username', __METHOD__ .
975  ": '$name' invalid due to wgInvalidUsernameCharacters" );
976  return false;
977  }
978  }
979 
980  return self::isUsableName( $name );
981  }
982 
989  public function isValidPassword( $password ) {
990  // simple boolean wrapper for getPasswordValidity
991  return $this->getPasswordValidity( $password ) === true;
992  }
993 
1000  public function getPasswordValidity( $password ) {
1001  $result = $this->checkPasswordValidity( $password );
1002  if ( $result->isGood() ) {
1003  return true;
1004  } else {
1005  $messages = [];
1006  foreach ( $result->getErrorsByType( 'error' ) as $error ) {
1007  $messages[] = $error['message'];
1008  }
1009  foreach ( $result->getErrorsByType( 'warning' ) as $warning ) {
1010  $messages[] = $warning['message'];
1011  }
1012  if ( count( $messages ) === 1 ) {
1013  return $messages[0];
1014  }
1015  return $messages;
1016  }
1017  }
1018 
1036  public function checkPasswordValidity( $password ) {
1037  global $wgPasswordPolicy;
1038 
1039  $upp = new UserPasswordPolicy(
1040  $wgPasswordPolicy['policies'],
1041  $wgPasswordPolicy['checks']
1042  );
1043 
1045  $result = false; // init $result to false for the internal checks
1046 
1047  if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) {
1048  $status->error( $result );
1049  return $status;
1050  }
1051 
1052  if ( $result === false ) {
1053  $status->merge( $upp->checkUserPassword( $this, $password ) );
1054  return $status;
1055  } elseif ( $result === true ) {
1056  return $status;
1057  } else {
1058  $status->error( $result );
1059  return $status; // the isValidPassword hook set a string $result and returned true
1060  }
1061  }
1062 
1076  public static function getCanonicalName( $name, $validate = 'valid' ) {
1077  // Force usernames to capital
1079  $name = $wgContLang->ucfirst( $name );
1080 
1081  # Reject names containing '#'; these will be cleaned up
1082  # with title normalisation, but then it's too late to
1083  # check elsewhere
1084  if ( strpos( $name, '#' ) !== false ) {
1085  return false;
1086  }
1087 
1088  // Clean up name according to title rules,
1089  // but only when validation is requested (T14654)
1090  $t = ( $validate !== false ) ?
1092  // Check for invalid titles
1093  if ( is_null( $t ) || $t->getNamespace() !== NS_USER || $t->isExternal() ) {
1094  return false;
1095  }
1096 
1097  // Reject various classes of invalid names
1098  $name = AuthManager::callLegacyAuthPlugin(
1099  'getCanonicalName', [ $t->getText() ], $t->getText()
1100  );
1101 
1102  switch ( $validate ) {
1103  case false:
1104  break;
1105  case 'valid':
1106  if ( !User::isValidUserName( $name ) ) {
1107  $name = false;
1108  }
1109  break;
1110  case 'usable':
1111  if ( !User::isUsableName( $name ) ) {
1112  $name = false;
1113  }
1114  break;
1115  case 'creatable':
1116  if ( !User::isCreatableName( $name ) ) {
1117  $name = false;
1118  }
1119  break;
1120  default:
1121  throw new InvalidArgumentException(
1122  'Invalid parameter value for $validate in ' . __METHOD__ );
1123  }
1124  return $name;
1125  }
1126 
1133  public static function randomPassword() {
1134  global $wgMinimalPasswordLength;
1135  return PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
1136  }
1137 
1146  public function loadDefaults( $name = false ) {
1147  $this->mId = 0;
1148  $this->mName = $name;
1149  $this->mRealName = '';
1150  $this->mEmail = '';
1151  $this->mOptionOverrides = null;
1152  $this->mOptionsLoaded = false;
1153 
1154  $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
1155  ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1156  if ( $loggedOut !== 0 ) {
1157  $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
1158  } else {
1159  $this->mTouched = '1'; # Allow any pages to be cached
1160  }
1161 
1162  $this->mToken = null; // Don't run cryptographic functions till we need a token
1163  $this->mEmailAuthenticated = null;
1164  $this->mEmailToken = '';
1165  $this->mEmailTokenExpires = null;
1166  $this->mRegistration = wfTimestamp( TS_MW );
1167  $this->mGroupMemberships = [];
1168 
1169  Hooks::run( 'UserLoadDefaults', [ $this, $name ] );
1170  }
1171 
1184  public function isItemLoaded( $item, $all = 'all' ) {
1185  return ( $this->mLoadedItems === true && $all === 'all' ) ||
1186  ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1187  }
1188 
1194  protected function setItemLoaded( $item ) {
1195  if ( is_array( $this->mLoadedItems ) ) {
1196  $this->mLoadedItems[$item] = true;
1197  }
1198  }
1199 
1205  private function loadFromSession() {
1206  // Deprecated hook
1207  $result = null;
1208  Hooks::run( 'UserLoadFromSession', [ $this, &$result ], '1.27' );
1209  if ( $result !== null ) {
1210  return $result;
1211  }
1212 
1213  // MediaWiki\Session\Session already did the necessary authentication of the user
1214  // returned here, so just use it if applicable.
1215  $session = $this->getRequest()->getSession();
1216  $user = $session->getUser();
1217  if ( $user->isLoggedIn() ) {
1218  $this->loadFromUserObject( $user );
1219 
1220  // If this user is autoblocked, set a cookie to track the Block. This has to be done on
1221  // every session load, because an autoblocked editor might not edit again from the same
1222  // IP address after being blocked.
1223  $config = RequestContext::getMain()->getConfig();
1224  if ( $config->get( 'CookieSetOnAutoblock' ) === true ) {
1225  $block = $this->getBlock();
1226  $shouldSetCookie = $this->getRequest()->getCookie( 'BlockID' ) === null
1227  && $block
1228  && $block->getType() === Block::TYPE_USER
1229  && $block->isAutoblocking();
1230  if ( $shouldSetCookie ) {
1231  wfDebug( __METHOD__ . ': User is autoblocked, setting cookie to track' );
1232  $block->setCookie( $this->getRequest()->response() );
1233  }
1234  }
1235 
1236  // Other code expects these to be set in the session, so set them.
1237  $session->set( 'wsUserID', $this->getId() );
1238  $session->set( 'wsUserName', $this->getName() );
1239  $session->set( 'wsToken', $this->getToken() );
1240  return true;
1241  }
1242  return false;
1243  }
1244 
1252  public function loadFromDatabase( $flags = self::READ_LATEST ) {
1253  // Paranoia
1254  $this->mId = intval( $this->mId );
1255 
1256  if ( !$this->mId ) {
1257  // Anonymous users are not in the database
1258  $this->loadDefaults();
1259  return false;
1260  }
1261 
1263  $db = wfGetDB( $index );
1264 
1265  $s = $db->selectRow(
1266  'user',
1267  self::selectFields(),
1268  [ 'user_id' => $this->mId ],
1269  __METHOD__,
1270  $options
1271  );
1272 
1273  $this->queryFlagsUsed = $flags;
1274  Hooks::run( 'UserLoadFromDatabase', [ $this, &$s ] );
1275 
1276  if ( $s !== false ) {
1277  // Initialise user table data
1278  $this->loadFromRow( $s );
1279  $this->mGroupMemberships = null; // deferred
1280  $this->getEditCount(); // revalidation for nulls
1281  return true;
1282  } else {
1283  // Invalid user_id
1284  $this->mId = 0;
1285  $this->loadDefaults();
1286  return false;
1287  }
1288  }
1289 
1302  protected function loadFromRow( $row, $data = null ) {
1303  $all = true;
1304 
1305  $this->mGroupMemberships = null; // deferred
1306 
1307  if ( isset( $row->user_name ) ) {
1308  $this->mName = $row->user_name;
1309  $this->mFrom = 'name';
1310  $this->setItemLoaded( 'name' );
1311  } else {
1312  $all = false;
1313  }
1314 
1315  if ( isset( $row->user_real_name ) ) {
1316  $this->mRealName = $row->user_real_name;
1317  $this->setItemLoaded( 'realname' );
1318  } else {
1319  $all = false;
1320  }
1321 
1322  if ( isset( $row->user_id ) ) {
1323  $this->mId = intval( $row->user_id );
1324  $this->mFrom = 'id';
1325  $this->setItemLoaded( 'id' );
1326  } else {
1327  $all = false;
1328  }
1329 
1330  if ( isset( $row->user_id ) && isset( $row->user_name ) ) {
1331  self::$idCacheByName[$row->user_name] = $row->user_id;
1332  }
1333 
1334  if ( isset( $row->user_editcount ) ) {
1335  $this->mEditCount = $row->user_editcount;
1336  } else {
1337  $all = false;
1338  }
1339 
1340  if ( isset( $row->user_touched ) ) {
1341  $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
1342  } else {
1343  $all = false;
1344  }
1345 
1346  if ( isset( $row->user_token ) ) {
1347  // The definition for the column is binary(32), so trim the NULs
1348  // that appends. The previous definition was char(32), so trim
1349  // spaces too.
1350  $this->mToken = rtrim( $row->user_token, " \0" );
1351  if ( $this->mToken === '' ) {
1352  $this->mToken = null;
1353  }
1354  } else {
1355  $all = false;
1356  }
1357 
1358  if ( isset( $row->user_email ) ) {
1359  $this->mEmail = $row->user_email;
1360  $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1361  $this->mEmailToken = $row->user_email_token;
1362  $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1363  $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
1364  } else {
1365  $all = false;
1366  }
1367 
1368  if ( $all ) {
1369  $this->mLoadedItems = true;
1370  }
1371 
1372  if ( is_array( $data ) ) {
1373  if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1374  if ( !count( $data['user_groups'] ) ) {
1375  $this->mGroupMemberships = [];
1376  } else {
1377  $firstGroup = reset( $data['user_groups'] );
1378  if ( is_array( $firstGroup ) || is_object( $firstGroup ) ) {
1379  $this->mGroupMemberships = [];
1380  foreach ( $data['user_groups'] as $row ) {
1381  $ugm = UserGroupMembership::newFromRow( (object)$row );
1382  $this->mGroupMemberships[$ugm->getGroup()] = $ugm;
1383  }
1384  }
1385  }
1386  }
1387  if ( isset( $data['user_properties'] ) && is_array( $data['user_properties'] ) ) {
1388  $this->loadOptions( $data['user_properties'] );
1389  }
1390  }
1391  }
1392 
1398  protected function loadFromUserObject( $user ) {
1399  $user->load();
1400  foreach ( self::$mCacheVars as $var ) {
1401  $this->$var = $user->$var;
1402  }
1403  }
1404 
1408  private function loadGroups() {
1409  if ( is_null( $this->mGroupMemberships ) ) {
1410  $db = ( $this->queryFlagsUsed & self::READ_LATEST )
1411  ? wfGetDB( DB_MASTER )
1412  : wfGetDB( DB_REPLICA );
1413  $this->mGroupMemberships = UserGroupMembership::getMembershipsForUser(
1414  $this->mId, $db );
1415  }
1416  }
1417 
1432  public function addAutopromoteOnceGroups( $event ) {
1433  global $wgAutopromoteOnceLogInRC;
1434 
1435  if ( wfReadOnly() || !$this->getId() ) {
1436  return [];
1437  }
1438 
1439  $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
1440  if ( !count( $toPromote ) ) {
1441  return [];
1442  }
1443 
1444  if ( !$this->checkAndSetTouched() ) {
1445  return []; // raced out (bug T48834)
1446  }
1447 
1448  $oldGroups = $this->getGroups(); // previous groups
1449  foreach ( $toPromote as $group ) {
1450  $this->addGroup( $group );
1451  }
1452  // update groups in external authentication database
1453  Hooks::run( 'UserGroupsChanged', [ $this, $toPromote, [], false, false ] );
1454  AuthManager::callLegacyAuthPlugin( 'updateExternalDBGroups', [ $this, $toPromote ] );
1455 
1456  $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
1457 
1458  $logEntry = new ManualLogEntry( 'rights', 'autopromote' );
1459  $logEntry->setPerformer( $this );
1460  $logEntry->setTarget( $this->getUserPage() );
1461  $logEntry->setParameters( [
1462  '4::oldgroups' => $oldGroups,
1463  '5::newgroups' => $newGroups,
1464  ] );
1465  $logid = $logEntry->insert();
1466  if ( $wgAutopromoteOnceLogInRC ) {
1467  $logEntry->publish( $logid );
1468  }
1469 
1470  return $toPromote;
1471  }
1472 
1482  protected function makeUpdateConditions( Database $db, array $conditions ) {
1483  if ( $this->mTouched ) {
1484  // CAS check: only update if the row wasn't changed sicne it was loaded.
1485  $conditions['user_touched'] = $db->timestamp( $this->mTouched );
1486  }
1487 
1488  return $conditions;
1489  }
1490 
1500  protected function checkAndSetTouched() {
1501  $this->load();
1502 
1503  if ( !$this->mId ) {
1504  return false; // anon
1505  }
1506 
1507  // Get a new user_touched that is higher than the old one
1508  $newTouched = $this->newTouchedTimestamp();
1509 
1510  $dbw = wfGetDB( DB_MASTER );
1511  $dbw->update( 'user',
1512  [ 'user_touched' => $dbw->timestamp( $newTouched ) ],
1513  $this->makeUpdateConditions( $dbw, [
1514  'user_id' => $this->mId,
1515  ] ),
1516  __METHOD__
1517  );
1518  $success = ( $dbw->affectedRows() > 0 );
1519 
1520  if ( $success ) {
1521  $this->mTouched = $newTouched;
1522  $this->clearSharedCache();
1523  } else {
1524  // Clears on failure too since that is desired if the cache is stale
1525  $this->clearSharedCache( 'refresh' );
1526  }
1527 
1528  return $success;
1529  }
1530 
1538  public function clearInstanceCache( $reloadFrom = false ) {
1539  $this->mNewtalk = -1;
1540  $this->mDatePreference = null;
1541  $this->mBlockedby = -1; # Unset
1542  $this->mHash = false;
1543  $this->mRights = null;
1544  $this->mEffectiveGroups = null;
1545  $this->mImplicitGroups = null;
1546  $this->mGroupMemberships = null;
1547  $this->mOptions = null;
1548  $this->mOptionsLoaded = false;
1549  $this->mEditCount = null;
1550 
1551  if ( $reloadFrom ) {
1552  $this->mLoadedItems = [];
1553  $this->mFrom = $reloadFrom;
1554  }
1555  }
1556 
1563  public static function getDefaultOptions() {
1564  global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin;
1565 
1566  static $defOpt = null;
1567  static $defOptLang = null;
1568 
1569  if ( $defOpt !== null && $defOptLang === $wgContLang->getCode() ) {
1570  // $wgContLang does not change (and should not change) mid-request,
1571  // but the unit tests change it anyway, and expect this method to
1572  // return values relevant to the current $wgContLang.
1573  return $defOpt;
1574  }
1575 
1576  $defOpt = $wgDefaultUserOptions;
1577  // Default language setting
1578  $defOptLang = $wgContLang->getCode();
1579  $defOpt['language'] = $defOptLang;
1580  foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
1581  $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
1582  }
1583 
1584  // NOTE: don't use SearchEngineConfig::getSearchableNamespaces here,
1585  // since extensions may change the set of searchable namespaces depending
1586  // on user groups/permissions.
1587  foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
1588  $defOpt['searchNs' . $nsnum] = (boolean)$val;
1589  }
1590  $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
1591 
1592  Hooks::run( 'UserGetDefaultOptions', [ &$defOpt ] );
1593 
1594  return $defOpt;
1595  }
1596 
1603  public static function getDefaultOption( $opt ) {
1604  $defOpts = self::getDefaultOptions();
1605  if ( isset( $defOpts[$opt] ) ) {
1606  return $defOpts[$opt];
1607  } else {
1608  return null;
1609  }
1610  }
1611 
1618  private function getBlockedStatus( $bFromSlave = true ) {
1619  global $wgProxyWhitelist, $wgUser, $wgApplyIpBlocksToXff, $wgSoftBlockRanges;
1620 
1621  if ( -1 != $this->mBlockedby ) {
1622  return;
1623  }
1624 
1625  wfDebug( __METHOD__ . ": checking...\n" );
1626 
1627  // Initialize data...
1628  // Otherwise something ends up stomping on $this->mBlockedby when
1629  // things get lazy-loaded later, causing false positive block hits
1630  // due to -1 !== 0. Probably session-related... Nothing should be
1631  // overwriting mBlockedby, surely?
1632  $this->load();
1633 
1634  # We only need to worry about passing the IP address to the Block generator if the
1635  # user is not immune to autoblocks/hardblocks, and they are the current user so we
1636  # know which IP address they're actually coming from
1637  $ip = null;
1638  if ( !$this->isAllowed( 'ipblock-exempt' ) ) {
1639  // $wgUser->getName() only works after the end of Setup.php. Until
1640  // then, assume it's a logged-out user.
1641  $globalUserName = $wgUser->isSafeToLoad()
1642  ? $wgUser->getName()
1643  : IP::sanitizeIP( $wgUser->getRequest()->getIP() );
1644  if ( $this->getName() === $globalUserName ) {
1645  $ip = $this->getRequest()->getIP();
1646  }
1647  }
1648 
1649  // User/IP blocking
1650  $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
1651 
1652  // Cookie blocking
1653  if ( !$block instanceof Block ) {
1654  $block = $this->getBlockFromCookieValue( $this->getRequest()->getCookie( 'BlockID' ) );
1655  }
1656 
1657  // Proxy blocking
1658  if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) {
1659  // Local list
1660  if ( self::isLocallyBlockedProxy( $ip ) ) {
1661  $block = new Block( [
1662  'byText' => wfMessage( 'proxyblocker' )->text(),
1663  'reason' => wfMessage( 'proxyblockreason' )->text(),
1664  'address' => $ip,
1665  'systemBlock' => 'proxy',
1666  ] );
1667  $this->blockTrigger = 'proxy-block';
1668  } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
1669  $block = new Block( [
1670  'byText' => wfMessage( 'sorbs' )->text(),
1671  'reason' => wfMessage( 'sorbsreason' )->text(),
1672  'address' => $ip,
1673  'systemBlock' => 'dnsbl',
1674  ] );
1675  $this->blockTrigger = 'openproxy-block';
1676  }
1677  }
1678 
1679  // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
1680  if ( !$block instanceof Block
1681  && $wgApplyIpBlocksToXff
1682  && $ip !== null
1683  && !in_array( $ip, $wgProxyWhitelist )
1684  ) {
1685  $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
1686  $xff = array_map( 'trim', explode( ',', $xff ) );
1687  $xff = array_diff( $xff, [ $ip ] );
1688  $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromSlave );
1689  $block = Block::chooseBlock( $xffblocks, $xff );
1690  if ( $block instanceof Block ) {
1691  # Mangle the reason to alert the user that the block
1692  # originated from matching the X-Forwarded-For header.
1693  $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->text();
1694  $this->blockTrigger = 'xff-block';
1695  }
1696  }
1697 
1698  if ( !$block instanceof Block
1699  && $ip !== null
1700  && $this->isAnon()
1701  && IP::isInRanges( $ip, $wgSoftBlockRanges )
1702  ) {
1703  $block = new Block( [
1704  'address' => $ip,
1705  'byText' => 'MediaWiki default',
1706  'reason' => wfMessage( 'softblockrangesreason', $ip )->text(),
1707  'anonOnly' => true,
1708  'systemBlock' => 'wgSoftBlockRanges',
1709  ] );
1710  $this->blockTrigger = 'config-block';
1711  }
1712 
1713  if ( $block instanceof Block ) {
1714  wfDebug( __METHOD__ . ": Found block.\n" );
1715  $this->mBlock = $block;
1716  $this->mBlockedby = $block->getByName();
1717  $this->mBlockreason = $block->mReason;
1718  $this->mHideName = $block->mHideName;
1719  $this->mAllowUsertalk = !$block->prevents( 'editownusertalk' );
1720  } else {
1721  $this->mBlockedby = '';
1722  $this->mHideName = 0;
1723  $this->mAllowUsertalk = false;
1724  $this->blockTrigger = false;
1725  }
1726 
1727  // Avoid PHP 7.1 warning of passing $this by reference
1728  $user = $this;
1729  // Extensions
1730  Hooks::run( 'GetBlockedStatus', [ &$user ] );
1731  }
1732 
1738  protected function getBlockFromCookieValue( $blockCookieVal ) {
1739  // Make sure there's something to check. The cookie value must start with a number.
1740  if ( strlen( $blockCookieVal ) < 1 || !is_numeric( substr( $blockCookieVal, 0, 1 ) ) ) {
1741  return false;
1742  }
1743  // Load the Block from the ID in the cookie.
1744  $blockCookieId = Block::getIdFromCookieValue( $blockCookieVal );
1745  if ( $blockCookieId !== null ) {
1746  // An ID was found in the cookie.
1747  $tmpBlock = Block::newFromID( $blockCookieId );
1748  if ( $tmpBlock instanceof Block ) {
1749  // Check the validity of the block.
1750  $blockIsValid = $tmpBlock->getType() == Block::TYPE_USER
1751  && !$tmpBlock->isExpired()
1752  && $tmpBlock->isAutoblocking();
1753  $config = RequestContext::getMain()->getConfig();
1754  $useBlockCookie = ( $config->get( 'CookieSetOnAutoblock' ) === true );
1755  if ( $blockIsValid && $useBlockCookie ) {
1756  // Use the block.
1757  $this->blockTrigger = 'cookie-block';
1758  return $tmpBlock;
1759  } else {
1760  // If the block is not valid, remove the cookie.
1761  Block::clearCookie( $this->getRequest()->response() );
1762  }
1763  } else {
1764  // If the block doesn't exist, remove the cookie.
1765  Block::clearCookie( $this->getRequest()->response() );
1766  }
1767  }
1768  return false;
1769  }
1770 
1778  public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
1779  global $wgEnableDnsBlacklist, $wgDnsBlacklistUrls, $wgProxyWhitelist;
1780 
1781  if ( !$wgEnableDnsBlacklist ) {
1782  return false;
1783  }
1784 
1785  if ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) ) {
1786  return false;
1787  }
1788 
1789  return $this->inDnsBlacklist( $ip, $wgDnsBlacklistUrls );
1790  }
1791 
1799  public function inDnsBlacklist( $ip, $bases ) {
1800  $found = false;
1801  // @todo FIXME: IPv6 ??? (https://bugs.php.net/bug.php?id=33170)
1802  if ( IP::isIPv4( $ip ) ) {
1803  // Reverse IP, T23255
1804  $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
1805 
1806  foreach ( (array)$bases as $base ) {
1807  // Make hostname
1808  // If we have an access key, use that too (ProjectHoneypot, etc.)
1809  $basename = $base;
1810  if ( is_array( $base ) ) {
1811  if ( count( $base ) >= 2 ) {
1812  // Access key is 1, base URL is 0
1813  $host = "{$base[1]}.$ipReversed.{$base[0]}";
1814  } else {
1815  $host = "$ipReversed.{$base[0]}";
1816  }
1817  $basename = $base[0];
1818  } else {
1819  $host = "$ipReversed.$base";
1820  }
1821 
1822  // Send query
1823  $ipList = gethostbynamel( $host );
1824 
1825  if ( $ipList ) {
1826  wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
1827  $found = true;
1828  break;
1829  } else {
1830  wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." );
1831  }
1832  }
1833  }
1834 
1835  return $found;
1836  }
1837 
1845  public static function isLocallyBlockedProxy( $ip ) {
1846  global $wgProxyList;
1847 
1848  if ( !$wgProxyList ) {
1849  return false;
1850  }
1851 
1852  if ( !is_array( $wgProxyList ) ) {
1853  // Load values from the specified file
1854  $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
1855  }
1856 
1857  if ( is_array( $wgProxyList ) ) {
1858  if (
1859  // Look for IP as value
1860  array_search( $ip, $wgProxyList ) !== false ||
1861  // Look for IP as key (for backwards-compatility)
1862  array_key_exists( $ip, $wgProxyList )
1863  ) {
1864  return true;
1865  }
1866  }
1867 
1868  return false;
1869  }
1870 
1876  public function isPingLimitable() {
1877  global $wgRateLimitsExcludedIPs;
1878  if ( IP::isInRanges( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
1879  // No other good way currently to disable rate limits
1880  // for specific IPs. :P
1881  // But this is a crappy hack and should die.
1882  return false;
1883  }
1884  return !$this->isAllowed( 'noratelimit' );
1885  }
1886 
1901  public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1902  // Avoid PHP 7.1 warning of passing $this by reference
1903  $user = $this;
1904  // Call the 'PingLimiter' hook
1905  $result = false;
1906  if ( !Hooks::run( 'PingLimiter', [ &$user, $action, &$result, $incrBy ] ) ) {
1907  return $result;
1908  }
1909 
1910  global $wgRateLimits;
1911  if ( !isset( $wgRateLimits[$action] ) ) {
1912  return false;
1913  }
1914 
1915  $limits = array_merge(
1916  [ '&can-bypass' => true ],
1917  $wgRateLimits[$action]
1918  );
1919 
1920  // Some groups shouldn't trigger the ping limiter, ever
1921  if ( $limits['&can-bypass'] && !$this->isPingLimitable() ) {
1922  return false;
1923  }
1924 
1925  $keys = [];
1926  $id = $this->getId();
1927  $userLimit = false;
1928  $isNewbie = $this->isNewbie();
1929 
1930  if ( $id == 0 ) {
1931  // limits for anons
1932  if ( isset( $limits['anon'] ) ) {
1933  $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
1934  }
1935  } else {
1936  // limits for logged-in users
1937  if ( isset( $limits['user'] ) ) {
1938  $userLimit = $limits['user'];
1939  }
1940  // limits for newbie logged-in users
1941  if ( $isNewbie && isset( $limits['newbie'] ) ) {
1942  $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
1943  }
1944  }
1945 
1946  // limits for anons and for newbie logged-in users
1947  if ( $isNewbie ) {
1948  // ip-based limits
1949  if ( isset( $limits['ip'] ) ) {
1950  $ip = $this->getRequest()->getIP();
1951  $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
1952  }
1953  // subnet-based limits
1954  if ( isset( $limits['subnet'] ) ) {
1955  $ip = $this->getRequest()->getIP();
1956  $subnet = IP::getSubnet( $ip );
1957  if ( $subnet !== false ) {
1958  $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
1959  }
1960  }
1961  }
1962 
1963  // Check for group-specific permissions
1964  // If more than one group applies, use the group with the highest limit ratio (max/period)
1965  foreach ( $this->getGroups() as $group ) {
1966  if ( isset( $limits[$group] ) ) {
1967  if ( $userLimit === false
1968  || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
1969  ) {
1970  $userLimit = $limits[$group];
1971  }
1972  }
1973  }
1974 
1975  // Set the user limit key
1976  if ( $userLimit !== false ) {
1977  list( $max, $period ) = $userLimit;
1978  wfDebug( __METHOD__ . ": effective user limit: $max in {$period}s\n" );
1979  $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $userLimit;
1980  }
1981 
1982  // ip-based limits for all ping-limitable users
1983  if ( isset( $limits['ip-all'] ) ) {
1984  $ip = $this->getRequest()->getIP();
1985  // ignore if user limit is more permissive
1986  if ( $isNewbie || $userLimit === false
1987  || $limits['ip-all'][0] / $limits['ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
1988  $keys["mediawiki:limiter:$action:ip-all:$ip"] = $limits['ip-all'];
1989  }
1990  }
1991 
1992  // subnet-based limits for all ping-limitable users
1993  if ( isset( $limits['subnet-all'] ) ) {
1994  $ip = $this->getRequest()->getIP();
1995  $subnet = IP::getSubnet( $ip );
1996  if ( $subnet !== false ) {
1997  // ignore if user limit is more permissive
1998  if ( $isNewbie || $userLimit === false
1999  || $limits['ip-all'][0] / $limits['ip-all'][1]
2000  > $userLimit[0] / $userLimit[1] ) {
2001  $keys["mediawiki:limiter:$action:subnet-all:$subnet"] = $limits['subnet-all'];
2002  }
2003  }
2004  }
2005 
2007 
2008  $triggered = false;
2009  foreach ( $keys as $key => $limit ) {
2010  list( $max, $period ) = $limit;
2011  $summary = "(limit $max in {$period}s)";
2012  $count = $cache->get( $key );
2013  // Already pinged?
2014  if ( $count ) {
2015  if ( $count >= $max ) {
2016  wfDebugLog( 'ratelimit', "User '{$this->getName()}' " .
2017  "(IP {$this->getRequest()->getIP()}) tripped $key at $count $summary" );
2018  $triggered = true;
2019  } else {
2020  wfDebug( __METHOD__ . ": ok. $key at $count $summary\n" );
2021  }
2022  } else {
2023  wfDebug( __METHOD__ . ": adding record for $key $summary\n" );
2024  if ( $incrBy > 0 ) {
2025  $cache->add( $key, 0, intval( $period ) ); // first ping
2026  }
2027  }
2028  if ( $incrBy > 0 ) {
2029  $cache->incr( $key, $incrBy );
2030  }
2031  }
2032 
2033  return $triggered;
2034  }
2035 
2043  public function isBlocked( $bFromSlave = true ) {
2044  return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
2045  }
2046 
2053  public function getBlock( $bFromSlave = true ) {
2054  $this->getBlockedStatus( $bFromSlave );
2055  return $this->mBlock instanceof Block ? $this->mBlock : null;
2056  }
2057 
2065  public function isBlockedFrom( $title, $bFromSlave = false ) {
2066  global $wgBlockAllowsUTEdit;
2067 
2068  $blocked = $this->isBlocked( $bFromSlave );
2069  $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
2070  // If a user's name is suppressed, they cannot make edits anywhere
2071  if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName()
2072  && $title->getNamespace() == NS_USER_TALK ) {
2073  $blocked = false;
2074  wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
2075  }
2076 
2077  Hooks::run( 'UserIsBlockedFrom', [ $this, $title, &$blocked, &$allowUsertalk ] );
2078 
2079  return $blocked;
2080  }
2081 
2086  public function blockedBy() {
2087  $this->getBlockedStatus();
2088  return $this->mBlockedby;
2089  }
2090 
2095  public function blockedFor() {
2096  $this->getBlockedStatus();
2097  return $this->mBlockreason;
2098  }
2099 
2104  public function getBlockId() {
2105  $this->getBlockedStatus();
2106  return ( $this->mBlock ? $this->mBlock->getId() : false );
2107  }
2108 
2117  public function isBlockedGlobally( $ip = '' ) {
2118  return $this->getGlobalBlock( $ip ) instanceof Block;
2119  }
2120 
2131  public function getGlobalBlock( $ip = '' ) {
2132  if ( $this->mGlobalBlock !== null ) {
2133  return $this->mGlobalBlock ?: null;
2134  }
2135  // User is already an IP?
2136  if ( IP::isIPAddress( $this->getName() ) ) {
2137  $ip = $this->getName();
2138  } elseif ( !$ip ) {
2139  $ip = $this->getRequest()->getIP();
2140  }
2141  // Avoid PHP 7.1 warning of passing $this by reference
2142  $user = $this;
2143  $blocked = false;
2144  $block = null;
2145  Hooks::run( 'UserIsBlockedGlobally', [ &$user, $ip, &$blocked, &$block ] );
2146 
2147  if ( $blocked && $block === null ) {
2148  // back-compat: UserIsBlockedGlobally didn't have $block param first
2149  $block = new Block( [
2150  'address' => $ip,
2151  'systemBlock' => 'global-block'
2152  ] );
2153  }
2154 
2155  $this->mGlobalBlock = $blocked ? $block : false;
2156  return $this->mGlobalBlock ?: null;
2157  }
2158 
2164  public function isLocked() {
2165  if ( $this->mLocked !== null ) {
2166  return $this->mLocked;
2167  }
2168  // Avoid PHP 7.1 warning of passing $this by reference
2169  $user = $this;
2170  $authUser = AuthManager::callLegacyAuthPlugin( 'getUserInstance', [ &$user ], null );
2171  $this->mLocked = $authUser && $authUser->isLocked();
2172  Hooks::run( 'UserIsLocked', [ $this, &$this->mLocked ] );
2173  return $this->mLocked;
2174  }
2175 
2181  public function isHidden() {
2182  if ( $this->mHideName !== null ) {
2183  return $this->mHideName;
2184  }
2185  $this->getBlockedStatus();
2186  if ( !$this->mHideName ) {
2187  // Avoid PHP 7.1 warning of passing $this by reference
2188  $user = $this;
2189  $authUser = AuthManager::callLegacyAuthPlugin( 'getUserInstance', [ &$user ], null );
2190  $this->mHideName = $authUser && $authUser->isHidden();
2191  Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
2192  }
2193  return $this->mHideName;
2194  }
2195 
2200  public function getId() {
2201  if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
2202  // Special case, we know the user is anonymous
2203  return 0;
2204  } elseif ( !$this->isItemLoaded( 'id' ) ) {
2205  // Don't load if this was initialized from an ID
2206  $this->load();
2207  }
2208 
2209  return (int)$this->mId;
2210  }
2211 
2216  public function setId( $v ) {
2217  $this->mId = $v;
2218  $this->clearInstanceCache( 'id' );
2219  }
2220 
2225  public function getName() {
2226  if ( $this->isItemLoaded( 'name', 'only' ) ) {
2227  // Special case optimisation
2228  return $this->mName;
2229  } else {
2230  $this->load();
2231  if ( $this->mName === false ) {
2232  // Clean up IPs
2233  $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
2234  }
2235  return $this->mName;
2236  }
2237  }
2238 
2252  public function setName( $str ) {
2253  $this->load();
2254  $this->mName = $str;
2255  }
2256 
2261  public function getTitleKey() {
2262  return str_replace( ' ', '_', $this->getName() );
2263  }
2264 
2269  public function getNewtalk() {
2270  $this->load();
2271 
2272  // Load the newtalk status if it is unloaded (mNewtalk=-1)
2273  if ( $this->mNewtalk === -1 ) {
2274  $this->mNewtalk = false; # reset talk page status
2275 
2276  // Check memcached separately for anons, who have no
2277  // entire User object stored in there.
2278  if ( !$this->mId ) {
2279  global $wgDisableAnonTalk;
2280  if ( $wgDisableAnonTalk ) {
2281  // Anon newtalk disabled by configuration.
2282  $this->mNewtalk = false;
2283  } else {
2284  $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName() );
2285  }
2286  } else {
2287  $this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
2288  }
2289  }
2290 
2291  return (bool)$this->mNewtalk;
2292  }
2293 
2307  public function getNewMessageLinks() {
2308  // Avoid PHP 7.1 warning of passing $this by reference
2309  $user = $this;
2310  $talks = [];
2311  if ( !Hooks::run( 'UserRetrieveNewTalks', [ &$user, &$talks ] ) ) {
2312  return $talks;
2313  } elseif ( !$this->getNewtalk() ) {
2314  return [];
2315  }
2316  $utp = $this->getTalkPage();
2317  $dbr = wfGetDB( DB_REPLICA );
2318  // Get the "last viewed rev" timestamp from the oldest message notification
2319  $timestamp = $dbr->selectField( 'user_newtalk',
2320  'MIN(user_last_timestamp)',
2321  $this->isAnon() ? [ 'user_ip' => $this->getName() ] : [ 'user_id' => $this->getId() ],
2322  __METHOD__ );
2323  $rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
2324  return [ [ 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ] ];
2325  }
2326 
2332  public function getNewMessageRevisionId() {
2333  $newMessageRevisionId = null;
2334  $newMessageLinks = $this->getNewMessageLinks();
2335  if ( $newMessageLinks ) {
2336  // Note: getNewMessageLinks() never returns more than a single link
2337  // and it is always for the same wiki, but we double-check here in
2338  // case that changes some time in the future.
2339  if ( count( $newMessageLinks ) === 1
2340  && $newMessageLinks[0]['wiki'] === wfWikiID()
2341  && $newMessageLinks[0]['rev']
2342  ) {
2344  $newMessageRevision = $newMessageLinks[0]['rev'];
2345  $newMessageRevisionId = $newMessageRevision->getId();
2346  }
2347  }
2348  return $newMessageRevisionId;
2349  }
2350 
2359  protected function checkNewtalk( $field, $id ) {
2360  $dbr = wfGetDB( DB_REPLICA );
2361 
2362  $ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
2363 
2364  return $ok !== false;
2365  }
2366 
2374  protected function updateNewtalk( $field, $id, $curRev = null ) {
2375  // Get timestamp of the talk page revision prior to the current one
2376  $prevRev = $curRev ? $curRev->getPrevious() : false;
2377  $ts = $prevRev ? $prevRev->getTimestamp() : null;
2378  // Mark the user as having new messages since this revision
2379  $dbw = wfGetDB( DB_MASTER );
2380  $dbw->insert( 'user_newtalk',
2381  [ $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ],
2382  __METHOD__,
2383  'IGNORE' );
2384  if ( $dbw->affectedRows() ) {
2385  wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
2386  return true;
2387  } else {
2388  wfDebug( __METHOD__ . " already set ($field, $id)\n" );
2389  return false;
2390  }
2391  }
2392 
2399  protected function deleteNewtalk( $field, $id ) {
2400  $dbw = wfGetDB( DB_MASTER );
2401  $dbw->delete( 'user_newtalk',
2402  [ $field => $id ],
2403  __METHOD__ );
2404  if ( $dbw->affectedRows() ) {
2405  wfDebug( __METHOD__ . ": killed on ($field, $id)\n" );
2406  return true;
2407  } else {
2408  wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
2409  return false;
2410  }
2411  }
2412 
2419  public function setNewtalk( $val, $curRev = null ) {
2420  if ( wfReadOnly() ) {
2421  return;
2422  }
2423 
2424  $this->load();
2425  $this->mNewtalk = $val;
2426 
2427  if ( $this->isAnon() ) {
2428  $field = 'user_ip';
2429  $id = $this->getName();
2430  } else {
2431  $field = 'user_id';
2432  $id = $this->getId();
2433  }
2434 
2435  if ( $val ) {
2436  $changed = $this->updateNewtalk( $field, $id, $curRev );
2437  } else {
2438  $changed = $this->deleteNewtalk( $field, $id );
2439  }
2440 
2441  if ( $changed ) {
2442  $this->invalidateCache();
2443  }
2444  }
2445 
2451  private function newTouchedTimestamp() {
2453 
2454  $time = wfTimestamp( TS_MW, time() + $wgClockSkewFudge );
2455  if ( $this->mTouched && $time <= $this->mTouched ) {
2456  $time = wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $this->mTouched ) + 1 );
2457  }
2458 
2459  return $time;
2460  }
2461 
2472  public function clearSharedCache( $mode = 'changed' ) {
2473  if ( !$this->getId() ) {
2474  return;
2475  }
2476 
2478  $key = $this->getCacheKey( $cache );
2479  if ( $mode === 'refresh' ) {
2480  $cache->delete( $key, 1 );
2481  } else {
2482  wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
2483  function() use ( $cache, $key ) {
2484  $cache->delete( $key );
2485  },
2486  __METHOD__
2487  );
2488  }
2489  }
2490 
2496  public function invalidateCache() {
2497  $this->touch();
2498  $this->clearSharedCache();
2499  }
2500 
2513  public function touch() {
2514  $id = $this->getId();
2515  if ( $id ) {
2516  $key = wfMemcKey( 'user-quicktouched', 'id', $id );
2517  ObjectCache::getMainWANInstance()->touchCheckKey( $key );
2518  $this->mQuickTouched = null;
2519  }
2520  }
2521 
2527  public function validateCache( $timestamp ) {
2528  return ( $timestamp >= $this->getTouched() );
2529  }
2530 
2539  public function getTouched() {
2540  $this->load();
2541 
2542  if ( $this->mId ) {
2543  if ( $this->mQuickTouched === null ) {
2544  $key = wfMemcKey( 'user-quicktouched', 'id', $this->mId );
2546 
2547  $this->mQuickTouched = wfTimestamp( TS_MW, $cache->getCheckKeyTime( $key ) );
2548  }
2549 
2550  return max( $this->mTouched, $this->mQuickTouched );
2551  }
2552 
2553  return $this->mTouched;
2554  }
2555 
2561  public function getDBTouched() {
2562  $this->load();
2563 
2564  return $this->mTouched;
2565  }
2566 
2583  public function setPassword( $str ) {
2584  return $this->setPasswordInternal( $str );
2585  }
2586 
2595  public function setInternalPassword( $str ) {
2596  $this->setPasswordInternal( $str );
2597  }
2598 
2607  private function setPasswordInternal( $str ) {
2608  $manager = AuthManager::singleton();
2609 
2610  // If the user doesn't exist yet, fail
2611  if ( !$manager->userExists( $this->getName() ) ) {
2612  throw new LogicException( 'Cannot set a password for a user that is not in the database.' );
2613  }
2614 
2615  $status = $this->changeAuthenticationData( [
2616  'username' => $this->getName(),
2617  'password' => $str,
2618  'retype' => $str,
2619  ] );
2620  if ( !$status->isGood() ) {
2622  ->info( __METHOD__ . ': Password change rejected: '
2623  . $status->getWikiText( null, null, 'en' ) );
2624  return false;
2625  }
2626 
2627  $this->setOption( 'watchlisttoken', false );
2628  SessionManager::singleton()->invalidateSessionsForUser( $this );
2629 
2630  return true;
2631  }
2632 
2645  public function changeAuthenticationData( array $data ) {
2646  $manager = AuthManager::singleton();
2647  $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2648  $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2649 
2650  $status = Status::newGood( 'ignored' );
2651  foreach ( $reqs as $req ) {
2652  $status->merge( $manager->allowsAuthenticationDataChange( $req ), true );
2653  }
2654  if ( $status->getValue() === 'ignored' ) {
2655  $status->warning( 'authenticationdatachange-ignored' );
2656  }
2657 
2658  if ( $status->isGood() ) {
2659  foreach ( $reqs as $req ) {
2660  $manager->changeAuthenticationData( $req );
2661  }
2662  }
2663  return $status;
2664  }
2665 
2672  public function getToken( $forceCreation = true ) {
2673  global $wgAuthenticationTokenVersion;
2674 
2675  $this->load();
2676  if ( !$this->mToken && $forceCreation ) {
2677  $this->setToken();
2678  }
2679 
2680  if ( !$this->mToken ) {
2681  // The user doesn't have a token, return null to indicate that.
2682  return null;
2683  } elseif ( $this->mToken === self::INVALID_TOKEN ) {
2684  // We return a random value here so existing token checks are very
2685  // likely to fail.
2686  return MWCryptRand::generateHex( self::TOKEN_LENGTH );
2687  } elseif ( $wgAuthenticationTokenVersion === null ) {
2688  // $wgAuthenticationTokenVersion not in use, so return the raw secret
2689  return $this->mToken;
2690  } else {
2691  // $wgAuthenticationTokenVersion in use, so hmac it.
2692  $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
2693 
2694  // The raw hash can be overly long. Shorten it up.
2695  $len = max( 32, self::TOKEN_LENGTH );
2696  if ( strlen( $ret ) < $len ) {
2697  // Should never happen, even md5 is 128 bits
2698  throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
2699  }
2700  return substr( $ret, -$len );
2701  }
2702  }
2703 
2710  public function setToken( $token = false ) {
2711  $this->load();
2712  if ( $this->mToken === self::INVALID_TOKEN ) {
2714  ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
2715  } elseif ( !$token ) {
2716  $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
2717  } else {
2718  $this->mToken = $token;
2719  }
2720  }
2721 
2730  public function setNewpassword( $str, $throttle = true ) {
2731  throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
2732  }
2733 
2738  public function getEmail() {
2739  $this->load();
2740  Hooks::run( 'UserGetEmail', [ $this, &$this->mEmail ] );
2741  return $this->mEmail;
2742  }
2743 
2749  $this->load();
2750  Hooks::run( 'UserGetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
2752  }
2753 
2758  public function setEmail( $str ) {
2759  $this->load();
2760  if ( $str == $this->mEmail ) {
2761  return;
2762  }
2763  $this->invalidateEmail();
2764  $this->mEmail = $str;
2765  Hooks::run( 'UserSetEmail', [ $this, &$this->mEmail ] );
2766  }
2767 
2775  public function setEmailWithConfirmation( $str ) {
2777 
2778  if ( !$wgEnableEmail ) {
2779  return Status::newFatal( 'emaildisabled' );
2780  }
2781 
2782  $oldaddr = $this->getEmail();
2783  if ( $str === $oldaddr ) {
2784  return Status::newGood( true );
2785  }
2786 
2787  $type = $oldaddr != '' ? 'changed' : 'set';
2788  $notificationResult = null;
2789 
2790  if ( $wgEmailAuthentication ) {
2791  // Send the user an email notifying the user of the change in registered
2792  // email address on their previous email address
2793  if ( $type == 'changed' ) {
2794  $change = $str != '' ? 'changed' : 'removed';
2795  $notificationResult = $this->sendMail(
2796  wfMessage( 'notificationemail_subject_' . $change )->text(),
2797  wfMessage( 'notificationemail_body_' . $change,
2798  $this->getRequest()->getIP(),
2799  $this->getName(),
2800  $str )->text()
2801  );
2802  }
2803  }
2804 
2805  $this->setEmail( $str );
2806 
2807  if ( $str !== '' && $wgEmailAuthentication ) {
2808  // Send a confirmation request to the new address if needed
2809  $result = $this->sendConfirmationMail( $type );
2810 
2811  if ( $notificationResult !== null ) {
2812  $result->merge( $notificationResult );
2813  }
2814 
2815  if ( $result->isGood() ) {
2816  // Say to the caller that a confirmation and notification mail has been sent
2817  $result->value = 'eauth';
2818  }
2819  } else {
2820  $result = Status::newGood( true );
2821  }
2822 
2823  return $result;
2824  }
2825 
2830  public function getRealName() {
2831  if ( !$this->isItemLoaded( 'realname' ) ) {
2832  $this->load();
2833  }
2834 
2835  return $this->mRealName;
2836  }
2837 
2842  public function setRealName( $str ) {
2843  $this->load();
2844  $this->mRealName = $str;
2845  }
2846 
2857  public function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
2858  global $wgHiddenPrefs;
2859  $this->loadOptions();
2860 
2861  # We want 'disabled' preferences to always behave as the default value for
2862  # users, even if they have set the option explicitly in their settings (ie they
2863  # set it, and then it was disabled removing their ability to change it). But
2864  # we don't want to erase the preferences in the database in case the preference
2865  # is re-enabled again. So don't touch $mOptions, just override the returned value
2866  if ( !$ignoreHidden && in_array( $oname, $wgHiddenPrefs ) ) {
2867  return self::getDefaultOption( $oname );
2868  }
2869 
2870  if ( array_key_exists( $oname, $this->mOptions ) ) {
2871  return $this->mOptions[$oname];
2872  } else {
2873  return $defaultOverride;
2874  }
2875  }
2876 
2885  public function getOptions( $flags = 0 ) {
2886  global $wgHiddenPrefs;
2887  $this->loadOptions();
2889 
2890  # We want 'disabled' preferences to always behave as the default value for
2891  # users, even if they have set the option explicitly in their settings (ie they
2892  # set it, and then it was disabled removing their ability to change it). But
2893  # we don't want to erase the preferences in the database in case the preference
2894  # is re-enabled again. So don't touch $mOptions, just override the returned value
2895  foreach ( $wgHiddenPrefs as $pref ) {
2896  $default = self::getDefaultOption( $pref );
2897  if ( $default !== null ) {
2898  $options[$pref] = $default;
2899  }
2900  }
2901 
2902  if ( $flags & self::GETOPTIONS_EXCLUDE_DEFAULTS ) {
2903  $options = array_diff_assoc( $options, self::getDefaultOptions() );
2904  }
2905 
2906  return $options;
2907  }
2908 
2916  public function getBoolOption( $oname ) {
2917  return (bool)$this->getOption( $oname );
2918  }
2919 
2928  public function getIntOption( $oname, $defaultOverride = 0 ) {
2929  $val = $this->getOption( $oname );
2930  if ( $val == '' ) {
2931  $val = $defaultOverride;
2932  }
2933  return intval( $val );
2934  }
2935 
2944  public function setOption( $oname, $val ) {
2945  $this->loadOptions();
2946 
2947  // Explicitly NULL values should refer to defaults
2948  if ( is_null( $val ) ) {
2949  $val = self::getDefaultOption( $oname );
2950  }
2951 
2952  $this->mOptions[$oname] = $val;
2953  }
2954 
2965  public function getTokenFromOption( $oname ) {
2966  global $wgHiddenPrefs;
2967 
2968  $id = $this->getId();
2969  if ( !$id || in_array( $oname, $wgHiddenPrefs ) ) {
2970  return false;
2971  }
2972 
2973  $token = $this->getOption( $oname );
2974  if ( !$token ) {
2975  // Default to a value based on the user token to avoid space
2976  // wasted on storing tokens for all users. When this option
2977  // is set manually by the user, only then is it stored.
2978  $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
2979  }
2980 
2981  return $token;
2982  }
2983 
2993  public function resetTokenFromOption( $oname ) {
2994  global $wgHiddenPrefs;
2995  if ( in_array( $oname, $wgHiddenPrefs ) ) {
2996  return false;
2997  }
2998 
2999  $token = MWCryptRand::generateHex( 40 );
3000  $this->setOption( $oname, $token );
3001  return $token;
3002  }
3003 
3027  public static function listOptionKinds() {
3028  return [
3029  'registered',
3030  'registered-multiselect',
3031  'registered-checkmatrix',
3032  'userjs',
3033  'special',
3034  'unused'
3035  ];
3036  }
3037 
3050  public function getOptionKinds( IContextSource $context, $options = null ) {
3051  $this->loadOptions();
3052  if ( $options === null ) {
3054  }
3055 
3056  $prefs = Preferences::getPreferences( $this, $context );
3057  $mapping = [];
3058 
3059  // Pull out the "special" options, so they don't get converted as
3060  // multiselect or checkmatrix.
3061  $specialOptions = array_fill_keys( Preferences::getSaveBlacklist(), true );
3062  foreach ( $specialOptions as $name => $value ) {
3063  unset( $prefs[$name] );
3064  }
3065 
3066  // Multiselect and checkmatrix options are stored in the database with
3067  // one key per option, each having a boolean value. Extract those keys.
3068  $multiselectOptions = [];
3069  foreach ( $prefs as $name => $info ) {
3070  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
3071  ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
3072  $opts = HTMLFormField::flattenOptions( $info['options'] );
3073  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
3074 
3075  foreach ( $opts as $value ) {
3076  $multiselectOptions["$prefix$value"] = true;
3077  }
3078 
3079  unset( $prefs[$name] );
3080  }
3081  }
3082  $checkmatrixOptions = [];
3083  foreach ( $prefs as $name => $info ) {
3084  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
3085  ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
3086  $columns = HTMLFormField::flattenOptions( $info['columns'] );
3087  $rows = HTMLFormField::flattenOptions( $info['rows'] );
3088  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
3089 
3090  foreach ( $columns as $column ) {
3091  foreach ( $rows as $row ) {
3092  $checkmatrixOptions["$prefix$column-$row"] = true;
3093  }
3094  }
3095 
3096  unset( $prefs[$name] );
3097  }
3098  }
3099 
3100  // $value is ignored
3101  foreach ( $options as $key => $value ) {
3102  if ( isset( $prefs[$key] ) ) {
3103  $mapping[$key] = 'registered';
3104  } elseif ( isset( $multiselectOptions[$key] ) ) {
3105  $mapping[$key] = 'registered-multiselect';
3106  } elseif ( isset( $checkmatrixOptions[$key] ) ) {
3107  $mapping[$key] = 'registered-checkmatrix';
3108  } elseif ( isset( $specialOptions[$key] ) ) {
3109  $mapping[$key] = 'special';
3110  } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
3111  $mapping[$key] = 'userjs';
3112  } else {
3113  $mapping[$key] = 'unused';
3114  }
3115  }
3116 
3117  return $mapping;
3118  }
3119 
3134  public function resetOptions(
3135  $resetKinds = [ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ],
3136  IContextSource $context = null
3137  ) {
3138  $this->load();
3139  $defaultOptions = self::getDefaultOptions();
3140 
3141  if ( !is_array( $resetKinds ) ) {
3142  $resetKinds = [ $resetKinds ];
3143  }
3144 
3145  if ( in_array( 'all', $resetKinds ) ) {
3146  $newOptions = $defaultOptions;
3147  } else {
3148  if ( $context === null ) {
3150  }
3151 
3152  $optionKinds = $this->getOptionKinds( $context );
3153  $resetKinds = array_intersect( $resetKinds, self::listOptionKinds() );
3154  $newOptions = [];
3155 
3156  // Use default values for the options that should be deleted, and
3157  // copy old values for the ones that shouldn't.
3158  foreach ( $this->mOptions as $key => $value ) {
3159  if ( in_array( $optionKinds[$key], $resetKinds ) ) {
3160  if ( array_key_exists( $key, $defaultOptions ) ) {
3161  $newOptions[$key] = $defaultOptions[$key];
3162  }
3163  } else {
3164  $newOptions[$key] = $value;
3165  }
3166  }
3167  }
3168 
3169  Hooks::run( 'UserResetAllOptions', [ $this, &$newOptions, $this->mOptions, $resetKinds ] );
3170 
3171  $this->mOptions = $newOptions;
3172  $this->mOptionsLoaded = true;
3173  }
3174 
3179  public function getDatePreference() {
3180  // Important migration for old data rows
3181  if ( is_null( $this->mDatePreference ) ) {
3182  global $wgLang;
3183  $value = $this->getOption( 'date' );
3184  $map = $wgLang->getDatePreferenceMigrationMap();
3185  if ( isset( $map[$value] ) ) {
3186  $value = $map[$value];
3187  }
3188  $this->mDatePreference = $value;
3189  }
3190  return $this->mDatePreference;
3191  }
3192 
3199  public function requiresHTTPS() {
3200  global $wgSecureLogin;
3201  if ( !$wgSecureLogin ) {
3202  return false;
3203  } else {
3204  $https = $this->getBoolOption( 'prefershttps' );
3205  Hooks::run( 'UserRequiresHTTPS', [ $this, &$https ] );
3206  if ( $https ) {
3207  $https = wfCanIPUseHTTPS( $this->getRequest()->getIP() );
3208  }
3209  return $https;
3210  }
3211  }
3212 
3218  public function getStubThreshold() {
3219  global $wgMaxArticleSize; # Maximum article size, in Kb
3220  $threshold = $this->getIntOption( 'stubthreshold' );
3221  if ( $threshold > $wgMaxArticleSize * 1024 ) {
3222  // If they have set an impossible value, disable the preference
3223  // so we can use the parser cache again.
3224  $threshold = 0;
3225  }
3226  return $threshold;
3227  }
3228 
3233  public function getRights() {
3234  if ( is_null( $this->mRights ) ) {
3235  $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
3236  Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
3237 
3238  // Deny any rights denied by the user's session, unless this
3239  // endpoint has no sessions.
3240  if ( !defined( 'MW_NO_SESSION' ) ) {
3241  $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
3242  if ( $allowedRights !== null ) {
3243  $this->mRights = array_intersect( $this->mRights, $allowedRights );
3244  }
3245  }
3246 
3247  // Force reindexation of rights when a hook has unset one of them
3248  $this->mRights = array_values( array_unique( $this->mRights ) );
3249 
3250  // If block disables login, we should also remove any
3251  // extra rights blocked users might have, in case the
3252  // blocked user has a pre-existing session (T129738).
3253  // This is checked here for cases where people only call
3254  // $user->isAllowed(). It is also checked in Title::checkUserBlock()
3255  // to give a better error message in the common case.
3256  $config = RequestContext::getMain()->getConfig();
3257  if (
3258  $this->isLoggedIn() &&
3259  $config->get( 'BlockDisablesLogin' ) &&
3260  $this->isBlocked()
3261  ) {
3262  $anon = new User;
3263  $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
3264  }
3265  }
3266  return $this->mRights;
3267  }
3268 
3274  public function getGroups() {
3275  $this->load();
3276  $this->loadGroups();
3277  return array_keys( $this->mGroupMemberships );
3278  }
3279 
3287  public function getGroupMemberships() {
3288  $this->load();
3289  $this->loadGroups();
3290  return $this->mGroupMemberships;
3291  }
3292 
3300  public function getEffectiveGroups( $recache = false ) {
3301  if ( $recache || is_null( $this->mEffectiveGroups ) ) {
3302  $this->mEffectiveGroups = array_unique( array_merge(
3303  $this->getGroups(), // explicit groups
3304  $this->getAutomaticGroups( $recache ) // implicit groups
3305  ) );
3306  // Avoid PHP 7.1 warning of passing $this by reference
3307  $user = $this;
3308  // Hook for additional groups
3309  Hooks::run( 'UserEffectiveGroups', [ &$user, &$this->mEffectiveGroups ] );
3310  // Force reindexation of groups when a hook has unset one of them
3311  $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
3312  }
3313  return $this->mEffectiveGroups;
3314  }
3315 
3323  public function getAutomaticGroups( $recache = false ) {
3324  if ( $recache || is_null( $this->mImplicitGroups ) ) {
3325  $this->mImplicitGroups = [ '*' ];
3326  if ( $this->getId() ) {
3327  $this->mImplicitGroups[] = 'user';
3328 
3329  $this->mImplicitGroups = array_unique( array_merge(
3330  $this->mImplicitGroups,
3332  ) );
3333  }
3334  if ( $recache ) {
3335  // Assure data consistency with rights/groups,
3336  // as getEffectiveGroups() depends on this function
3337  $this->mEffectiveGroups = null;
3338  }
3339  }
3340  return $this->mImplicitGroups;
3341  }
3342 
3352  public function getFormerGroups() {
3353  $this->load();
3354 
3355  if ( is_null( $this->mFormerGroups ) ) {
3356  $db = ( $this->queryFlagsUsed & self::READ_LATEST )
3357  ? wfGetDB( DB_MASTER )
3358  : wfGetDB( DB_REPLICA );
3359  $res = $db->select( 'user_former_groups',
3360  [ 'ufg_group' ],
3361  [ 'ufg_user' => $this->mId ],
3362  __METHOD__ );
3363  $this->mFormerGroups = [];
3364  foreach ( $res as $row ) {
3365  $this->mFormerGroups[] = $row->ufg_group;
3366  }
3367  }
3368 
3369  return $this->mFormerGroups;
3370  }
3371 
3376  public function getEditCount() {
3377  if ( !$this->getId() ) {
3378  return null;
3379  }
3380 
3381  if ( $this->mEditCount === null ) {
3382  /* Populate the count, if it has not been populated yet */
3383  $dbr = wfGetDB( DB_REPLICA );
3384  // check if the user_editcount field has been initialized
3385  $count = $dbr->selectField(
3386  'user', 'user_editcount',
3387  [ 'user_id' => $this->mId ],
3388  __METHOD__
3389  );
3390 
3391  if ( $count === null ) {
3392  // it has not been initialized. do so.
3393  $count = $this->initEditCount();
3394  }
3395  $this->mEditCount = $count;
3396  }
3397  return (int)$this->mEditCount;
3398  }
3399 
3411  public function addGroup( $group, $expiry = null ) {
3412  $this->load();
3413  $this->loadGroups();
3414 
3415  if ( $expiry ) {
3416  $expiry = wfTimestamp( TS_MW, $expiry );
3417  }
3418 
3419  if ( !Hooks::run( 'UserAddGroup', [ $this, &$group, &$expiry ] ) ) {
3420  return false;
3421  }
3422 
3423  // create the new UserGroupMembership and put it in the DB
3424  $ugm = new UserGroupMembership( $this->mId, $group, $expiry );
3425  if ( !$ugm->insert( true ) ) {
3426  return false;
3427  }
3428 
3429  $this->mGroupMemberships[$group] = $ugm;
3430 
3431  // Refresh the groups caches, and clear the rights cache so it will be
3432  // refreshed on the next call to $this->getRights().
3433  $this->getEffectiveGroups( true );
3434  $this->mRights = null;
3435 
3436  $this->invalidateCache();
3437 
3438  return true;
3439  }
3440 
3447  public function removeGroup( $group ) {
3448  $this->load();
3449 
3450  if ( !Hooks::run( 'UserRemoveGroup', [ $this, &$group ] ) ) {
3451  return false;
3452  }
3453 
3454  $ugm = UserGroupMembership::getMembership( $this->mId, $group );
3455  // delete the membership entry
3456  if ( !$ugm || !$ugm->delete() ) {
3457  return false;
3458  }
3459 
3460  $this->loadGroups();
3461  unset( $this->mGroupMemberships[$group] );
3462 
3463  // Refresh the groups caches, and clear the rights cache so it will be
3464  // refreshed on the next call to $this->getRights().
3465  $this->getEffectiveGroups( true );
3466  $this->mRights = null;
3467 
3468  $this->invalidateCache();
3469 
3470  return true;
3471  }
3472 
3477  public function isLoggedIn() {
3478  return $this->getId() != 0;
3479  }
3480 
3485  public function isAnon() {
3486  return !$this->isLoggedIn();
3487  }
3488 
3493  public function isBot() {
3494  if ( in_array( 'bot', $this->getGroups() ) && $this->isAllowed( 'bot' ) ) {
3495  return true;
3496  }
3497 
3498  $isBot = false;
3499  Hooks::run( "UserIsBot", [ $this, &$isBot ] );
3500 
3501  return $isBot;
3502  }
3503 
3510  public function isAllowedAny() {
3511  $permissions = func_get_args();
3512  foreach ( $permissions as $permission ) {
3513  if ( $this->isAllowed( $permission ) ) {
3514  return true;
3515  }
3516  }
3517  return false;
3518  }
3519 
3525  public function isAllowedAll() {
3526  $permissions = func_get_args();
3527  foreach ( $permissions as $permission ) {
3528  if ( !$this->isAllowed( $permission ) ) {
3529  return false;
3530  }
3531  }
3532  return true;
3533  }
3534 
3540  public function isAllowed( $action = '' ) {
3541  if ( $action === '' ) {
3542  return true; // In the spirit of DWIM
3543  }
3544  // Use strict parameter to avoid matching numeric 0 accidentally inserted
3545  // by misconfiguration: 0 == 'foo'
3546  return in_array( $action, $this->getRights(), true );
3547  }
3548 
3553  public function useRCPatrol() {
3554  global $wgUseRCPatrol;
3555  return $wgUseRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
3556  }
3557 
3562  public function useNPPatrol() {
3563  global $wgUseRCPatrol, $wgUseNPPatrol;
3564  return (
3565  ( $wgUseRCPatrol || $wgUseNPPatrol )
3566  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3567  );
3568  }
3569 
3574  public function useFilePatrol() {
3575  global $wgUseRCPatrol, $wgUseFilePatrol;
3576  return (
3577  ( $wgUseRCPatrol || $wgUseFilePatrol )
3578  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3579  );
3580  }
3581 
3587  public function getRequest() {
3588  if ( $this->mRequest ) {
3589  return $this->mRequest;
3590  } else {
3592  return $wgRequest;
3593  }
3594  }
3595 
3604  public function isWatched( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3605  if ( $title->isWatchable() && ( !$checkRights || $this->isAllowed( 'viewmywatchlist' ) ) ) {
3606  return MediaWikiServices::getInstance()->getWatchedItemStore()->isWatched( $this, $title );
3607  }
3608  return false;
3609  }
3610 
3618  public function addWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3619  if ( !$checkRights || $this->isAllowed( 'editmywatchlist' ) ) {
3620  MediaWikiServices::getInstance()->getWatchedItemStore()->addWatchBatchForUser(
3621  $this,
3622  [ $title->getSubjectPage(), $title->getTalkPage() ]
3623  );
3624  }
3625  $this->invalidateCache();
3626  }
3627 
3635  public function removeWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3636  if ( !$checkRights || $this->isAllowed( 'editmywatchlist' ) ) {
3637  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3638  $store->removeWatch( $this, $title->getSubjectPage() );
3639  $store->removeWatch( $this, $title->getTalkPage() );
3640  }
3641  $this->invalidateCache();
3642  }
3643 
3652  public function clearNotification( &$title, $oldid = 0 ) {
3653  global $wgUseEnotif, $wgShowUpdatedMarker;
3654 
3655  // Do nothing if the database is locked to writes
3656  if ( wfReadOnly() ) {
3657  return;
3658  }
3659 
3660  // Do nothing if not allowed to edit the watchlist
3661  if ( !$this->isAllowed( 'editmywatchlist' ) ) {
3662  return;
3663  }
3664 
3665  // If we're working on user's talk page, we should update the talk page message indicator
3666  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3667  // Avoid PHP 7.1 warning of passing $this by reference
3668  $user = $this;
3669  if ( !Hooks::run( 'UserClearNewTalkNotification', [ &$user, $oldid ] ) ) {
3670  return;
3671  }
3672 
3673  // Try to update the DB post-send and only if needed...
3674  DeferredUpdates::addCallableUpdate( function() use ( $title, $oldid ) {
3675  if ( !$this->getNewtalk() ) {
3676  return; // no notifications to clear
3677  }
3678 
3679  // Delete the last notifications (they stack up)
3680  $this->setNewtalk( false );
3681 
3682  // If there is a new, unseen, revision, use its timestamp
3683  $nextid = $oldid
3684  ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
3685  : null;
3686  if ( $nextid ) {
3687  $this->setNewtalk( true, Revision::newFromId( $nextid ) );
3688  }
3689  } );
3690  }
3691 
3692  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3693  return;
3694  }
3695 
3696  if ( $this->isAnon() ) {
3697  // Nothing else to do...
3698  return;
3699  }
3700 
3701  // Only update the timestamp if the page is being watched.
3702  // The query to find out if it is watched is cached both in memcached and per-invocation,
3703  // and when it does have to be executed, it can be on a replica DB
3704  // If this is the user's newtalk page, we always update the timestamp
3705  $force = '';
3706  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3707  $force = 'force';
3708  }
3709 
3710  MediaWikiServices::getInstance()->getWatchedItemStore()
3711  ->resetNotificationTimestamp( $this, $title, $force, $oldid );
3712  }
3713 
3720  public function clearAllNotifications() {
3721  global $wgUseEnotif, $wgShowUpdatedMarker;
3722  // Do nothing if not allowed to edit the watchlist
3723  if ( wfReadOnly() || !$this->isAllowed( 'editmywatchlist' ) ) {
3724  return;
3725  }
3726 
3727  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3728  $this->setNewtalk( false );
3729  return;
3730  }
3731 
3732  $id = $this->getId();
3733  if ( !$id ) {
3734  return;
3735  }
3736 
3737  $dbw = wfGetDB( DB_MASTER );
3738  $asOfTimes = array_unique( $dbw->selectFieldValues(
3739  'watchlist',
3740  'wl_notificationtimestamp',
3741  [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
3742  __METHOD__,
3743  [ 'ORDER BY' => 'wl_notificationtimestamp DESC', 'LIMIT' => 500 ]
3744  ) );
3745  if ( !$asOfTimes ) {
3746  return;
3747  }
3748  // Immediately update the most recent touched rows, which hopefully covers what
3749  // the user sees on the watchlist page before pressing "mark all pages visited"....
3750  $dbw->update(
3751  'watchlist',
3752  [ 'wl_notificationtimestamp' => null ],
3753  [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimes ],
3754  __METHOD__
3755  );
3756  // ...and finish the older ones in a post-send update with lag checks...
3758  $dbw,
3759  __METHOD__,
3760  function () use ( $dbw, $id ) {
3761  global $wgUpdateRowsPerQuery;
3762 
3763  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3764  $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
3765  $asOfTimes = array_unique( $dbw->selectFieldValues(
3766  'watchlist',
3767  'wl_notificationtimestamp',
3768  [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
3769  __METHOD__
3770  ) );
3771  foreach ( array_chunk( $asOfTimes, $wgUpdateRowsPerQuery ) as $asOfTimeBatch ) {
3772  $dbw->update(
3773  'watchlist',
3774  [ 'wl_notificationtimestamp' => null ],
3775  [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimeBatch ],
3776  __METHOD__
3777  );
3778  $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
3779  }
3780  }
3781  ) );
3782  // We also need to clear here the "you have new message" notification for the own
3783  // user_talk page; it's cleared one page view later in WikiPage::doViewUpdates().
3784  }
3785 
3791  public function getExperienceLevel() {
3792  global $wgLearnerEdits,
3793  $wgExperiencedUserEdits,
3794  $wgLearnerMemberSince,
3795  $wgExperiencedUserMemberSince;
3796 
3797  if ( $this->isAnon() ) {
3798  return false;
3799  }
3800 
3801  $editCount = $this->getEditCount();
3802  $registration = $this->getRegistration();
3803  $now = time();
3804  $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
3805  $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
3806 
3807  if (
3808  $editCount < $wgLearnerEdits ||
3809  $registration > $learnerRegistration
3810  ) {
3811  return 'newcomer';
3812  } elseif (
3813  $editCount > $wgExperiencedUserEdits &&
3814  $registration <= $experiencedRegistration
3815  ) {
3816  return 'experienced';
3817  } else {
3818  return 'learner';
3819  }
3820  }
3821 
3838  protected function setCookie(
3839  $name, $value, $exp = 0, $secure = null, $params = [], $request = null
3840  ) {
3841  wfDeprecated( __METHOD__, '1.27' );
3842  if ( $request === null ) {
3843  $request = $this->getRequest();
3844  }
3845  $params['secure'] = $secure;
3846  $request->response()->setCookie( $name, $value, $exp, $params );
3847  }
3848 
3859  protected function clearCookie( $name, $secure = null, $params = [] ) {
3860  wfDeprecated( __METHOD__, '1.27' );
3861  $this->setCookie( $name, '', time() - 86400, $secure, $params );
3862  }
3863 
3879  protected function setExtendedLoginCookie( $name, $value, $secure ) {
3880  global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
3881 
3882  wfDeprecated( __METHOD__, '1.27' );
3883 
3884  $exp = time();
3885  $exp += $wgExtendedLoginCookieExpiration !== null
3886  ? $wgExtendedLoginCookieExpiration
3887  : $wgCookieExpiration;
3888 
3889  $this->setCookie( $name, $value, $exp, $secure );
3890  }
3891 
3900  public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
3901  $this->load();
3902  if ( 0 == $this->mId ) {
3903  return;
3904  }
3905 
3906  $session = $this->getRequest()->getSession();
3907  if ( $request && $session->getRequest() !== $request ) {
3908  $session = $session->sessionWithRequest( $request );
3909  }
3910  $delay = $session->delaySave();
3911 
3912  if ( !$session->getUser()->equals( $this ) ) {
3913  if ( !$session->canSetUser() ) {
3915  ->warning( __METHOD__ .
3916  ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3917  );
3918  return;
3919  }
3920  $session->setUser( $this );
3921  }
3922 
3923  $session->setRememberUser( $rememberMe );
3924  if ( $secure !== null ) {
3925  $session->setForceHTTPS( $secure );
3926  }
3927 
3928  $session->persist();
3929 
3930  ScopedCallback::consume( $delay );
3931  }
3932 
3936  public function logout() {
3937  // Avoid PHP 7.1 warning of passing $this by reference
3938  $user = $this;
3939  if ( Hooks::run( 'UserLogout', [ &$user ] ) ) {
3940  $this->doLogout();
3941  }
3942  }
3943 
3948  public function doLogout() {
3949  $session = $this->getRequest()->getSession();
3950  if ( !$session->canSetUser() ) {
3952  ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
3953  $error = 'immutable';
3954  } elseif ( !$session->getUser()->equals( $this ) ) {
3956  ->warning( __METHOD__ .
3957  ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
3958  );
3959  // But we still may as well make this user object anon
3960  $this->clearInstanceCache( 'defaults' );
3961  $error = 'wronguser';
3962  } else {
3963  $this->clearInstanceCache( 'defaults' );
3964  $delay = $session->delaySave();
3965  $session->unpersist(); // Clear cookies (T127436)
3966  $session->setLoggedOutTimestamp( time() );
3967  $session->setUser( new User );
3968  $session->set( 'wsUserID', 0 ); // Other code expects this
3969  $session->resetAllTokens();
3970  ScopedCallback::consume( $delay );
3971  $error = false;
3972  }
3973  \MediaWiki\Logger\LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
3974  'event' => 'logout',
3975  'successful' => $error === false,
3976  'status' => $error ?: 'success',
3977  ] );
3978  }
3979 
3984  public function saveSettings() {
3985  if ( wfReadOnly() ) {
3986  // @TODO: caller should deal with this instead!
3987  // This should really just be an exception.
3989  null,
3990  "Could not update user with ID '{$this->mId}'; DB is read-only."
3991  ) );
3992  return;
3993  }
3994 
3995  $this->load();
3996  if ( 0 == $this->mId ) {
3997  return; // anon
3998  }
3999 
4000  // Get a new user_touched that is higher than the old one.
4001  // This will be used for a CAS check as a last-resort safety
4002  // check against race conditions and replica DB lag.
4003  $newTouched = $this->newTouchedTimestamp();
4004 
4005  $dbw = wfGetDB( DB_MASTER );
4006  $dbw->update( 'user',
4007  [ /* SET */
4008  'user_name' => $this->mName,
4009  'user_real_name' => $this->mRealName,
4010  'user_email' => $this->mEmail,
4011  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
4012  'user_touched' => $dbw->timestamp( $newTouched ),
4013  'user_token' => strval( $this->mToken ),
4014  'user_email_token' => $this->mEmailToken,
4015  'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
4016  ], $this->makeUpdateConditions( $dbw, [ /* WHERE */
4017  'user_id' => $this->mId,
4018  ] ), __METHOD__
4019  );
4020 
4021  if ( !$dbw->affectedRows() ) {
4022  // Maybe the problem was a missed cache update; clear it to be safe
4023  $this->clearSharedCache( 'refresh' );
4024  // User was changed in the meantime or loaded with stale data
4025  $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
4026  throw new MWException(
4027  "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
4028  " the version of the user to be saved is older than the current version."
4029  );
4030  }
4031 
4032  $this->mTouched = $newTouched;
4033  $this->saveOptions();
4034 
4035  Hooks::run( 'UserSaveSettings', [ $this ] );
4036  $this->clearSharedCache();
4037  $this->getUserPage()->invalidateCache();
4038  }
4039 
4046  public function idForName( $flags = 0 ) {
4047  $s = trim( $this->getName() );
4048  if ( $s === '' ) {
4049  return 0;
4050  }
4051 
4052  $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
4053  ? wfGetDB( DB_MASTER )
4054  : wfGetDB( DB_REPLICA );
4055 
4056  $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
4057  ? [ 'LOCK IN SHARE MODE' ]
4058  : [];
4059 
4060  $id = $db->selectField( 'user',
4061  'user_id', [ 'user_name' => $s ], __METHOD__, $options );
4062 
4063  return (int)$id;
4064  }
4065 
4081  public static function createNew( $name, $params = [] ) {
4082  foreach ( [ 'password', 'newpassword', 'newpass_time', 'password_expires' ] as $field ) {
4083  if ( isset( $params[$field] ) ) {
4084  wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
4085  unset( $params[$field] );
4086  }
4087  }
4088 
4089  $user = new User;
4090  $user->load();
4091  $user->setToken(); // init token
4092  if ( isset( $params['options'] ) ) {
4093  $user->mOptions = $params['options'] + (array)$user->mOptions;
4094  unset( $params['options'] );
4095  }
4096  $dbw = wfGetDB( DB_MASTER );
4097  $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
4098 
4099  $noPass = PasswordFactory::newInvalidPassword()->toString();
4100 
4101  $fields = [
4102  'user_id' => $seqVal,
4103  'user_name' => $name,
4104  'user_password' => $noPass,
4105  'user_newpassword' => $noPass,
4106  'user_email' => $user->mEmail,
4107  'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
4108  'user_real_name' => $user->mRealName,
4109  'user_token' => strval( $user->mToken ),
4110  'user_registration' => $dbw->timestamp( $user->mRegistration ),
4111  'user_editcount' => 0,
4112  'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
4113  ];
4114  foreach ( $params as $name => $value ) {
4115  $fields["user_$name"] = $value;
4116  }
4117  $dbw->insert( 'user', $fields, __METHOD__, [ 'IGNORE' ] );
4118  if ( $dbw->affectedRows() ) {
4119  $newUser = User::newFromId( $dbw->insertId() );
4120  } else {
4121  $newUser = null;
4122  }
4123  return $newUser;
4124  }
4125 
4152  public function addToDatabase() {
4153  $this->load();
4154  if ( !$this->mToken ) {
4155  $this->setToken(); // init token
4156  }
4157 
4158  $this->mTouched = $this->newTouchedTimestamp();
4159 
4160  $noPass = PasswordFactory::newInvalidPassword()->toString();
4161 
4162  $dbw = wfGetDB( DB_MASTER );
4163  $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
4164  $dbw->insert( 'user',
4165  [
4166  'user_id' => $seqVal,
4167  'user_name' => $this->mName,
4168  'user_password' => $noPass,
4169  'user_newpassword' => $noPass,
4170  'user_email' => $this->mEmail,
4171  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
4172  'user_real_name' => $this->mRealName,
4173  'user_token' => strval( $this->mToken ),
4174  'user_registration' => $dbw->timestamp( $this->mRegistration ),
4175  'user_editcount' => 0,
4176  'user_touched' => $dbw->timestamp( $this->mTouched ),
4177  ], __METHOD__,
4178  [ 'IGNORE' ]
4179  );
4180  if ( !$dbw->affectedRows() ) {
4181  // Use locking reads to bypass any REPEATABLE-READ snapshot.
4182  $this->mId = $dbw->selectField(
4183  'user',
4184  'user_id',
4185  [ 'user_name' => $this->mName ],
4186  __METHOD__,
4187  [ 'LOCK IN SHARE MODE' ]
4188  );
4189  $loaded = false;
4190  if ( $this->mId ) {
4191  if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
4192  $loaded = true;
4193  }
4194  }
4195  if ( !$loaded ) {
4196  throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
4197  "to insert user '{$this->mName}' row, but it was not present in select!" );
4198  }
4199  return Status::newFatal( 'userexists' );
4200  }
4201  $this->mId = $dbw->insertId();
4202  self::$idCacheByName[$this->mName] = $this->mId;
4203 
4204  // Clear instance cache other than user table data, which is already accurate
4205  $this->clearInstanceCache();
4206 
4207  $this->saveOptions();
4208  return Status::newGood();
4209  }
4210 
4216  public function spreadAnyEditBlock() {
4217  if ( $this->isLoggedIn() && $this->isBlocked() ) {
4218  return $this->spreadBlock();
4219  }
4220 
4221  return false;
4222  }
4223 
4229  protected function spreadBlock() {
4230  wfDebug( __METHOD__ . "()\n" );
4231  $this->load();
4232  if ( $this->mId == 0 ) {
4233  return false;
4234  }
4235 
4236  $userblock = Block::newFromTarget( $this->getName() );
4237  if ( !$userblock ) {
4238  return false;
4239  }
4240 
4241  return (bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
4242  }
4243 
4248  public function isBlockedFromCreateAccount() {
4249  $this->getBlockedStatus();
4250  if ( $this->mBlock && $this->mBlock->prevents( 'createaccount' ) ) {
4251  return $this->mBlock;
4252  }
4253 
4254  # T15611: if the IP address the user is trying to create an account from is
4255  # blocked with createaccount disabled, prevent new account creation there even
4256  # when the user is logged in
4257  if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
4258  $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
4259  }
4260  return $this->mBlockedFromCreateAccount instanceof Block
4261  && $this->mBlockedFromCreateAccount->prevents( 'createaccount' )
4262  ? $this->mBlockedFromCreateAccount
4263  : false;
4264  }
4265 
4270  public function isBlockedFromEmailuser() {
4271  $this->getBlockedStatus();
4272  return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
4273  }
4274 
4279  public function isAllowedToCreateAccount() {
4280  return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
4281  }
4282 
4288  public function getUserPage() {
4289  return Title::makeTitle( NS_USER, $this->getName() );
4290  }
4291 
4297  public function getTalkPage() {
4298  $title = $this->getUserPage();
4299  return $title->getTalkPage();
4300  }
4301 
4307  public function isNewbie() {
4308  return !$this->isAllowed( 'autoconfirmed' );
4309  }
4310 
4317  public function checkPassword( $password ) {
4318  $manager = AuthManager::singleton();
4319  $reqs = AuthenticationRequest::loadRequestsFromSubmission(
4320  $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
4321  [
4322  'username' => $this->getName(),
4323  'password' => $password,
4324  ]
4325  );
4326  $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
4327  switch ( $res->status ) {
4328  case AuthenticationResponse::PASS:
4329  return true;
4330  case AuthenticationResponse::FAIL:
4331  // Hope it's not a PreAuthenticationProvider that failed...
4333  ->info( __METHOD__ . ': Authentication failed: ' . $res->message->plain() );
4334  return false;
4335  default:
4336  throw new BadMethodCallException(
4337  'AuthManager returned a response unsupported by ' . __METHOD__
4338  );
4339  }
4340  }
4341 
4350  public function checkTemporaryPassword( $plaintext ) {
4351  // Can't check the temporary password individually.
4352  return $this->checkPassword( $plaintext );
4353  }
4354 
4366  public function getEditTokenObject( $salt = '', $request = null ) {
4367  if ( $this->isAnon() ) {
4368  return new LoggedOutEditToken();
4369  }
4370 
4371  if ( !$request ) {
4372  $request = $this->getRequest();
4373  }
4374  return $request->getSession()->getToken( $salt );
4375  }
4376 
4390  public function getEditToken( $salt = '', $request = null ) {
4391  return $this->getEditTokenObject( $salt, $request )->toString();
4392  }
4393 
4400  public static function getEditTokenTimestamp( $val ) {
4401  wfDeprecated( __METHOD__, '1.27' );
4403  }
4404 
4417  public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
4418  return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
4419  }
4420 
4431  public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
4432  $val = substr( $val, 0, strspn( $val, '0123456789abcdef' ) ) . Token::SUFFIX;
4433  return $this->matchEditToken( $val, $salt, $request, $maxage );
4434  }
4435 
4443  public function sendConfirmationMail( $type = 'created' ) {
4444  global $wgLang;
4445  $expiration = null; // gets passed-by-ref and defined in next line.
4446  $token = $this->confirmationToken( $expiration );
4447  $url = $this->confirmationTokenUrl( $token );
4448  $invalidateURL = $this->invalidationTokenUrl( $token );
4449  $this->saveSettings();
4450 
4451  if ( $type == 'created' || $type === false ) {
4452  $message = 'confirmemail_body';
4453  } elseif ( $type === true ) {
4454  $message = 'confirmemail_body_changed';
4455  } else {
4456  // Messages: confirmemail_body_changed, confirmemail_body_set
4457  $message = 'confirmemail_body_' . $type;
4458  }
4459 
4460  return $this->sendMail( wfMessage( 'confirmemail_subject' )->text(),
4461  wfMessage( $message,
4462  $this->getRequest()->getIP(),
4463  $this->getName(),
4464  $url,
4465  $wgLang->userTimeAndDate( $expiration, $this ),
4466  $invalidateURL,
4467  $wgLang->userDate( $expiration, $this ),
4468  $wgLang->userTime( $expiration, $this ) )->text() );
4469  }
4470 
4482  public function sendMail( $subject, $body, $from = null, $replyto = null ) {
4484 
4485  if ( $from instanceof User ) {
4486  $sender = MailAddress::newFromUser( $from );
4487  } else {
4488  $sender = new MailAddress( $wgPasswordSender,
4489  wfMessage( 'emailsender' )->inContentLanguage()->text() );
4490  }
4491  $to = MailAddress::newFromUser( $this );
4492 
4493  return UserMailer::send( $to, $sender, $subject, $body, [
4494  'replyTo' => $replyto,
4495  ] );
4496  }
4497 
4508  protected function confirmationToken( &$expiration ) {
4510  $now = time();
4511  $expires = $now + $wgUserEmailConfirmationTokenExpiry;
4512  $expiration = wfTimestamp( TS_MW, $expires );
4513  $this->load();
4514  $token = MWCryptRand::generateHex( 32 );
4515  $hash = md5( $token );
4516  $this->mEmailToken = $hash;
4517  $this->mEmailTokenExpires = $expiration;
4518  return $token;
4519  }
4520 
4526  protected function confirmationTokenUrl( $token ) {
4527  return $this->getTokenUrl( 'ConfirmEmail', $token );
4528  }
4529 
4535  protected function invalidationTokenUrl( $token ) {
4536  return $this->getTokenUrl( 'InvalidateEmail', $token );
4537  }
4538 
4553  protected function getTokenUrl( $page, $token ) {
4554  // Hack to bypass localization of 'Special:'
4555  $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
4556  return $title->getCanonicalURL();
4557  }
4558 
4566  public function confirmEmail() {
4567  // Check if it's already confirmed, so we don't touch the database
4568  // and fire the ConfirmEmailComplete hook on redundant confirmations.
4569  if ( !$this->isEmailConfirmed() ) {
4571  Hooks::run( 'ConfirmEmailComplete', [ $this ] );
4572  }
4573  return true;
4574  }
4575 
4583  public function invalidateEmail() {
4584  $this->load();
4585  $this->mEmailToken = null;
4586  $this->mEmailTokenExpires = null;
4587  $this->setEmailAuthenticationTimestamp( null );
4588  $this->mEmail = '';
4589  Hooks::run( 'InvalidateEmailComplete', [ $this ] );
4590  return true;
4591  }
4592 
4597  public function setEmailAuthenticationTimestamp( $timestamp ) {
4598  $this->load();
4599  $this->mEmailAuthenticated = $timestamp;
4600  Hooks::run( 'UserSetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
4601  }
4602 
4608  public function canSendEmail() {
4610  if ( !$wgEnableEmail || !$wgEnableUserEmail || !$this->isAllowed( 'sendemail' ) ) {
4611  return false;
4612  }
4613  $canSend = $this->isEmailConfirmed();
4614  // Avoid PHP 7.1 warning of passing $this by reference
4615  $user = $this;
4616  Hooks::run( 'UserCanSendEmail', [ &$user, &$canSend ] );
4617  return $canSend;
4618  }
4619 
4625  public function canReceiveEmail() {
4626  return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
4627  }
4628 
4639  public function isEmailConfirmed() {
4641  $this->load();
4642  // Avoid PHP 7.1 warning of passing $this by reference
4643  $user = $this;
4644  $confirmed = true;
4645  if ( Hooks::run( 'EmailConfirmed', [ &$user, &$confirmed ] ) ) {
4646  if ( $this->isAnon() ) {
4647  return false;
4648  }
4649  if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4650  return false;
4651  }
4653  return false;
4654  }
4655  return true;
4656  } else {
4657  return $confirmed;
4658  }
4659  }
4660 
4665  public function isEmailConfirmationPending() {
4667  return $wgEmailAuthentication &&
4668  !$this->isEmailConfirmed() &&
4669  $this->mEmailToken &&
4670  $this->mEmailTokenExpires > wfTimestamp();
4671  }
4672 
4680  public function getRegistration() {
4681  if ( $this->isAnon() ) {
4682  return false;
4683  }
4684  $this->load();
4685  return $this->mRegistration;
4686  }
4687 
4694  public function getFirstEditTimestamp() {
4695  if ( $this->getId() == 0 ) {
4696  return false; // anons
4697  }
4698  $dbr = wfGetDB( DB_REPLICA );
4699  $time = $dbr->selectField( 'revision', 'rev_timestamp',
4700  [ 'rev_user' => $this->getId() ],
4701  __METHOD__,
4702  [ 'ORDER BY' => 'rev_timestamp ASC' ]
4703  );
4704  if ( !$time ) {
4705  return false; // no edits
4706  }
4707  return wfTimestamp( TS_MW, $time );
4708  }
4709 
4716  public static function getGroupPermissions( $groups ) {
4717  global $wgGroupPermissions, $wgRevokePermissions;
4718  $rights = [];
4719  // grant every granted permission first
4720  foreach ( $groups as $group ) {
4721  if ( isset( $wgGroupPermissions[$group] ) ) {
4722  $rights = array_merge( $rights,
4723  // array_filter removes empty items
4724  array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
4725  }
4726  }
4727  // now revoke the revoked permissions
4728  foreach ( $groups as $group ) {
4729  if ( isset( $wgRevokePermissions[$group] ) ) {
4730  $rights = array_diff( $rights,
4731  array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
4732  }
4733  }
4734  return array_unique( $rights );
4735  }
4736 
4743  public static function getGroupsWithPermission( $role ) {
4744  global $wgGroupPermissions;
4745  $allowedGroups = [];
4746  foreach ( array_keys( $wgGroupPermissions ) as $group ) {
4747  if ( self::groupHasPermission( $group, $role ) ) {
4748  $allowedGroups[] = $group;
4749  }
4750  }
4751  return $allowedGroups;
4752  }
4753 
4766  public static function groupHasPermission( $group, $role ) {
4767  global $wgGroupPermissions, $wgRevokePermissions;
4768  return isset( $wgGroupPermissions[$group][$role] ) && $wgGroupPermissions[$group][$role]
4769  && !( isset( $wgRevokePermissions[$group][$role] ) && $wgRevokePermissions[$group][$role] );
4770  }
4771 
4786  public static function isEveryoneAllowed( $right ) {
4787  global $wgGroupPermissions, $wgRevokePermissions;
4788  static $cache = [];
4789 
4790  // Use the cached results, except in unit tests which rely on
4791  // being able change the permission mid-request
4792  if ( isset( $cache[$right] ) && !defined( 'MW_PHPUNIT_TEST' ) ) {
4793  return $cache[$right];
4794  }
4795 
4796  if ( !isset( $wgGroupPermissions['*'][$right] ) || !$wgGroupPermissions['*'][$right] ) {
4797  $cache[$right] = false;
4798  return false;
4799  }
4800 
4801  // If it's revoked anywhere, then everyone doesn't have it
4802  foreach ( $wgRevokePermissions as $rights ) {
4803  if ( isset( $rights[$right] ) && $rights[$right] ) {
4804  $cache[$right] = false;
4805  return false;
4806  }
4807  }
4808 
4809  // Remove any rights that aren't allowed to the global-session user,
4810  // unless there are no sessions for this endpoint.
4811  if ( !defined( 'MW_NO_SESSION' ) ) {
4812  $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
4813  if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
4814  $cache[$right] = false;
4815  return false;
4816  }
4817  }
4818 
4819  // Allow extensions to say false
4820  if ( !Hooks::run( 'UserIsEveryoneAllowed', [ $right ] ) ) {
4821  $cache[$right] = false;
4822  return false;
4823  }
4824 
4825  $cache[$right] = true;
4826  return true;
4827  }
4828 
4836  public static function getGroupName( $group ) {
4837  wfDeprecated( __METHOD__, '1.29' );
4838  return UserGroupMembership::getGroupName( $group );
4839  }
4840 
4849  public static function getGroupMember( $group, $username = '#' ) {
4850  wfDeprecated( __METHOD__, '1.29' );
4852  }
4853 
4860  public static function getAllGroups() {
4861  global $wgGroupPermissions, $wgRevokePermissions;
4862  return array_diff(
4863  array_merge( array_keys( $wgGroupPermissions ), array_keys( $wgRevokePermissions ) ),
4864  self::getImplicitGroups()
4865  );
4866  }
4867 
4872  public static function getAllRights() {
4873  if ( self::$mAllRights === false ) {
4874  global $wgAvailableRights;
4875  if ( count( $wgAvailableRights ) ) {
4876  self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
4877  } else {
4878  self::$mAllRights = self::$mCoreRights;
4879  }
4880  Hooks::run( 'UserGetAllRights', [ &self::$mAllRights ] );
4881  }
4882  return self::$mAllRights;
4883  }
4884 
4889  public static function getImplicitGroups() {
4890  global $wgImplicitGroups;
4891 
4892  $groups = $wgImplicitGroups;
4893  # Deprecated, use $wgImplicitGroups instead
4894  Hooks::run( 'UserGetImplicitGroups', [ &$groups ], '1.25' );
4895 
4896  return $groups;
4897  }
4898 
4906  public static function getGroupPage( $group ) {
4907  wfDeprecated( __METHOD__, '1.29' );
4908  return UserGroupMembership::getGroupPage( $group );
4909  }
4910 
4921  public static function makeGroupLinkHTML( $group, $text = '' ) {
4922  wfDeprecated( __METHOD__, '1.29' );
4923 
4924  if ( $text == '' ) {
4925  $text = UserGroupMembership::getGroupName( $group );
4926  }
4928  if ( $title ) {
4929  return Linker::link( $title, htmlspecialchars( $text ) );
4930  } else {
4931  return htmlspecialchars( $text );
4932  }
4933  }
4934 
4945  public static function makeGroupLinkWiki( $group, $text = '' ) {
4946  wfDeprecated( __METHOD__, '1.29' );
4947 
4948  if ( $text == '' ) {
4949  $text = UserGroupMembership::getGroupName( $group );
4950  }
4952  if ( $title ) {
4953  $page = $title->getFullText();
4954  return "[[$page|$text]]";
4955  } else {
4956  return $text;
4957  }
4958  }
4959 
4969  public static function changeableByGroup( $group ) {
4970  global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
4971 
4972  $groups = [
4973  'add' => [],
4974  'remove' => [],
4975  'add-self' => [],
4976  'remove-self' => []
4977  ];
4978 
4979  if ( empty( $wgAddGroups[$group] ) ) {
4980  // Don't add anything to $groups
4981  } elseif ( $wgAddGroups[$group] === true ) {
4982  // You get everything
4983  $groups['add'] = self::getAllGroups();
4984  } elseif ( is_array( $wgAddGroups[$group] ) ) {
4985  $groups['add'] = $wgAddGroups[$group];
4986  }
4987 
4988  // Same thing for remove
4989  if ( empty( $wgRemoveGroups[$group] ) ) {
4990  // Do nothing
4991  } elseif ( $wgRemoveGroups[$group] === true ) {
4992  $groups['remove'] = self::getAllGroups();
4993  } elseif ( is_array( $wgRemoveGroups[$group] ) ) {
4994  $groups['remove'] = $wgRemoveGroups[$group];
4995  }
4996 
4997  // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
4998  if ( empty( $wgGroupsAddToSelf['user'] ) || $wgGroupsAddToSelf['user'] !== true ) {
4999  foreach ( $wgGroupsAddToSelf as $key => $value ) {
5000  if ( is_int( $key ) ) {
5001  $wgGroupsAddToSelf['user'][] = $value;
5002  }
5003  }
5004  }
5005 
5006  if ( empty( $wgGroupsRemoveFromSelf['user'] ) || $wgGroupsRemoveFromSelf['user'] !== true ) {
5007  foreach ( $wgGroupsRemoveFromSelf as $key => $value ) {
5008  if ( is_int( $key ) ) {
5009  $wgGroupsRemoveFromSelf['user'][] = $value;
5010  }
5011  }
5012  }
5013 
5014  // Now figure out what groups the user can add to him/herself
5015  if ( empty( $wgGroupsAddToSelf[$group] ) ) {
5016  // Do nothing
5017  } elseif ( $wgGroupsAddToSelf[$group] === true ) {
5018  // No idea WHY this would be used, but it's there
5019  $groups['add-self'] = User::getAllGroups();
5020  } elseif ( is_array( $wgGroupsAddToSelf[$group] ) ) {
5021  $groups['add-self'] = $wgGroupsAddToSelf[$group];
5022  }
5023 
5024  if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
5025  // Do nothing
5026  } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
5027  $groups['remove-self'] = User::getAllGroups();
5028  } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
5029  $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
5030  }
5031 
5032  return $groups;
5033  }
5034 
5042  public function changeableGroups() {
5043  if ( $this->isAllowed( 'userrights' ) ) {
5044  // This group gives the right to modify everything (reverse-
5045  // compatibility with old "userrights lets you change
5046  // everything")
5047  // Using array_merge to make the groups reindexed
5048  $all = array_merge( User::getAllGroups() );
5049  return [
5050  'add' => $all,
5051  'remove' => $all,
5052  'add-self' => [],
5053  'remove-self' => []
5054  ];
5055  }
5056 
5057  // Okay, it's not so simple, we will have to go through the arrays
5058  $groups = [
5059  'add' => [],
5060  'remove' => [],
5061  'add-self' => [],
5062  'remove-self' => []
5063  ];
5064  $addergroups = $this->getEffectiveGroups();
5065 
5066  foreach ( $addergroups as $addergroup ) {
5067  $groups = array_merge_recursive(
5068  $groups, $this->changeableByGroup( $addergroup )
5069  );
5070  $groups['add'] = array_unique( $groups['add'] );
5071  $groups['remove'] = array_unique( $groups['remove'] );
5072  $groups['add-self'] = array_unique( $groups['add-self'] );
5073  $groups['remove-self'] = array_unique( $groups['remove-self'] );
5074  }
5075  return $groups;
5076  }
5077 
5081  public function incEditCount() {
5082  wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
5083  function () {
5084  $this->incEditCountImmediate();
5085  },
5086  __METHOD__
5087  );
5088  }
5089 
5095  public function incEditCountImmediate() {
5096  if ( $this->isAnon() ) {
5097  return;
5098  }
5099 
5100  $dbw = wfGetDB( DB_MASTER );
5101  // No rows will be "affected" if user_editcount is NULL
5102  $dbw->update(
5103  'user',
5104  [ 'user_editcount=user_editcount+1' ],
5105  [ 'user_id' => $this->getId(), 'user_editcount IS NOT NULL' ],
5106  __METHOD__
5107  );
5108  // Lazy initialization check...
5109  if ( $dbw->affectedRows() == 0 ) {
5110  // Now here's a goddamn hack...
5111  $dbr = wfGetDB( DB_REPLICA );
5112  if ( $dbr !== $dbw ) {
5113  // If we actually have a replica DB server, the count is
5114  // at least one behind because the current transaction
5115  // has not been committed and replicated.
5116  $this->mEditCount = $this->initEditCount( 1 );
5117  } else {
5118  // But if DB_REPLICA is selecting the master, then the
5119  // count we just read includes the revision that was
5120  // just added in the working transaction.
5121  $this->mEditCount = $this->initEditCount();
5122  }
5123  } else {
5124  if ( $this->mEditCount === null ) {
5125  $this->getEditCount();
5126  $dbr = wfGetDB( DB_REPLICA );
5127  $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
5128  } else {
5129  $this->mEditCount++;
5130  }
5131  }
5132  // Edit count in user cache too
5133  $this->invalidateCache();
5134  }
5135 
5142  protected function initEditCount( $add = 0 ) {
5143  // Pull from a replica DB to be less cruel to servers
5144  // Accuracy isn't the point anyway here
5145  $dbr = wfGetDB( DB_REPLICA );
5146  $count = (int)$dbr->selectField(
5147  'revision',
5148  'COUNT(rev_user)',
5149  [ 'rev_user' => $this->getId() ],
5150  __METHOD__
5151  );
5152  $count = $count + $add;
5153 
5154  $dbw = wfGetDB( DB_MASTER );
5155  $dbw->update(
5156  'user',
5157  [ 'user_editcount' => $count ],
5158  [ 'user_id' => $this->getId() ],
5159  __METHOD__
5160  );
5161 
5162  return $count;
5163  }
5164 
5172  public static function getRightDescription( $right ) {
5173  $key = "right-$right";
5174  $msg = wfMessage( $key );
5175  return $msg->isDisabled() ? $right : $msg->text();
5176  }
5177 
5185  public static function getGrantName( $grant ) {
5186  $key = "grant-$grant";
5187  $msg = wfMessage( $key );
5188  return $msg->isDisabled() ? $grant : $msg->text();
5189  }
5190 
5211  public function addNewUserLogEntry( $action = false, $reason = '' ) {
5212  return true; // disabled
5213  }
5214 
5223  public function addNewUserLogEntryAutoCreate() {
5224  $this->addNewUserLogEntry( 'autocreate' );
5225 
5226  return true;
5227  }
5228 
5234  protected function loadOptions( $data = null ) {
5236 
5237  $this->load();
5238 
5239  if ( $this->mOptionsLoaded ) {
5240  return;
5241  }
5242 
5243  $this->mOptions = self::getDefaultOptions();
5244 
5245  if ( !$this->getId() ) {
5246  // For unlogged-in users, load language/variant options from request.
5247  // There's no need to do it for logged-in users: they can set preferences,
5248  // and handling of page content is done by $pageLang->getPreferredVariant() and such,
5249  // so don't override user's choice (especially when the user chooses site default).
5250  $variant = $wgContLang->getDefaultVariant();
5251  $this->mOptions['variant'] = $variant;
5252  $this->mOptions['language'] = $variant;
5253  $this->mOptionsLoaded = true;
5254  return;
5255  }
5256 
5257  // Maybe load from the object
5258  if ( !is_null( $this->mOptionOverrides ) ) {
5259  wfDebug( "User: loading options for user " . $this->getId() . " from override cache.\n" );
5260  foreach ( $this->mOptionOverrides as $key => $value ) {
5261  $this->mOptions[$key] = $value;
5262  }
5263  } else {
5264  if ( !is_array( $data ) ) {
5265  wfDebug( "User: loading options for user " . $this->getId() . " from database.\n" );
5266  // Load from database
5267  $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
5268  ? wfGetDB( DB_MASTER )
5269  : wfGetDB( DB_REPLICA );
5270 
5271  $res = $dbr->select(
5272  'user_properties',
5273  [ 'up_property', 'up_value' ],
5274  [ 'up_user' => $this->getId() ],
5275  __METHOD__
5276  );
5277 
5278  $this->mOptionOverrides = [];
5279  $data = [];
5280  foreach ( $res as $row ) {
5281  // Convert '0' to 0. PHP's boolean conversion considers them both
5282  // false, but e.g. JavaScript considers the former as true.
5283  // @todo: T54542 Somehow determine the desired type (string/int/bool)
5284  // and convert all values here.
5285  if ( $row->up_value === '0' ) {
5286  $row->up_value = 0;
5287  }
5288  $data[$row->up_property] = $row->up_value;
5289  }
5290  }
5291  foreach ( $data as $property => $value ) {
5292  $this->mOptionOverrides[$property] = $value;
5293  $this->mOptions[$property] = $value;
5294  }
5295  }
5296 
5297  $this->mOptionsLoaded = true;
5298 
5299  Hooks::run( 'UserLoadOptions', [ $this, &$this->mOptions ] );
5300  }
5301 
5307  protected function saveOptions() {
5308  $this->loadOptions();
5309 
5310  // Not using getOptions(), to keep hidden preferences in database
5311  $saveOptions = $this->mOptions;
5312 
5313  // Allow hooks to abort, for instance to save to a global profile.
5314  // Reset options to default state before saving.
5315  if ( !Hooks::run( 'UserSaveOptions', [ $this, &$saveOptions ] ) ) {
5316  return;
5317  }
5318 
5319  $userId = $this->getId();
5320 
5321  $insert_rows = []; // all the new preference rows
5322  foreach ( $saveOptions as $key => $value ) {
5323  // Don't bother storing default values
5324  $defaultOption = self::getDefaultOption( $key );
5325  if ( ( $defaultOption === null && $value !== false && $value !== null )
5326  || $value != $defaultOption
5327  ) {
5328  $insert_rows[] = [
5329  'up_user' => $userId,
5330  'up_property' => $key,
5331  'up_value' => $value,
5332  ];
5333  }
5334  }
5335 
5336  $dbw = wfGetDB( DB_MASTER );
5337 
5338  $res = $dbw->select( 'user_properties',
5339  [ 'up_property', 'up_value' ], [ 'up_user' => $userId ], __METHOD__ );
5340 
5341  // Find prior rows that need to be removed or updated. These rows will
5342  // all be deleted (the latter so that INSERT IGNORE applies the new values).
5343  $keysDelete = [];
5344  foreach ( $res as $row ) {
5345  if ( !isset( $saveOptions[$row->up_property] )
5346  || strcmp( $saveOptions[$row->up_property], $row->up_value ) != 0
5347  ) {
5348  $keysDelete[] = $row->up_property;
5349  }
5350  }
5351 
5352  if ( count( $keysDelete ) ) {
5353  // Do the DELETE by PRIMARY KEY for prior rows.
5354  // In the past a very large portion of calls to this function are for setting
5355  // 'rememberpassword' for new accounts (a preference that has since been removed).
5356  // Doing a blanket per-user DELETE for new accounts with no rows in the table
5357  // caused gap locks on [max user ID,+infinity) which caused high contention since
5358  // updates would pile up on each other as they are for higher (newer) user IDs.
5359  // It might not be necessary these days, but it shouldn't hurt either.
5360  $dbw->delete( 'user_properties',
5361  [ 'up_user' => $userId, 'up_property' => $keysDelete ], __METHOD__ );
5362  }
5363  // Insert the new preference rows
5364  $dbw->insert( 'user_properties', $insert_rows, __METHOD__, [ 'IGNORE' ] );
5365  }
5366 
5373  public static function getPasswordFactory() {
5374  wfDeprecated( __METHOD__, '1.27' );
5375  $ret = new PasswordFactory();
5376  $ret->init( RequestContext::getMain()->getConfig() );
5377  return $ret;
5378  }
5379 
5404  public static function passwordChangeInputAttribs() {
5405  global $wgMinimalPasswordLength;
5406 
5407  if ( $wgMinimalPasswordLength == 0 ) {
5408  return [];
5409  }
5410 
5411  # Note that the pattern requirement will always be satisfied if the
5412  # input is empty, so we need required in all cases.
5413 
5414  # @todo FIXME: T25769: This needs to not claim the password is required
5415  # if e-mail confirmation is being used. Since HTML5 input validation
5416  # is b0rked anyway in some browsers, just return nothing. When it's
5417  # re-enabled, fix this code to not output required for e-mail
5418  # registration.
5419  # $ret = array( 'required' );
5420  $ret = [];
5421 
5422  # We can't actually do this right now, because Opera 9.6 will print out
5423  # the entered password visibly in its error message! When other
5424  # browsers add support for this attribute, or Opera fixes its support,
5425  # we can add support with a version check to avoid doing this on Opera
5426  # versions where it will be a problem. Reported to Opera as
5427  # DSK-262266, but they don't have a public bug tracker for us to follow.
5428  /*
5429  if ( $wgMinimalPasswordLength > 1 ) {
5430  $ret['pattern'] = '.{' . intval( $wgMinimalPasswordLength ) . ',}';
5431  $ret['title'] = wfMessage( 'passwordtooshort' )
5432  ->numParams( $wgMinimalPasswordLength )->text();
5433  }
5434  */
5435 
5436  return $ret;
5437  }
5438 
5444  public static function selectFields() {
5445  return [
5446  'user_id',
5447  'user_name',
5448  'user_real_name',
5449  'user_email',
5450  'user_touched',
5451  'user_token',
5452  'user_email_authenticated',
5453  'user_email_token',
5454  'user_email_token_expires',
5455  'user_registration',
5456  'user_editcount',
5457  ];
5458  }
5459 
5467  static function newFatalPermissionDeniedStatus( $permission ) {
5468  global $wgLang;
5469 
5470  $groups = [];
5471  foreach ( User::getGroupsWithPermission( $permission ) as $group ) {
5472  $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(), 'wiki' );
5473  }
5474 
5475  if ( $groups ) {
5476  return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
5477  } else {
5478  return Status::newFatal( 'badaccess-group0' );
5479  }
5480  }
5481 
5491  public function getInstanceForUpdate() {
5492  if ( !$this->getId() ) {
5493  return null; // anon
5494  }
5495 
5496  $user = self::newFromId( $this->getId() );
5497  if ( !$user->loadFromId( self::READ_EXCLUSIVE ) ) {
5498  return null;
5499  }
5500 
5501  return $user;
5502  }
5503 
5511  public function equals( User $user ) {
5512  return $this->getName() === $user->getName();
5513  }
5514 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1603
User\saveOptions
saveOptions()
Saves the non-default options for this user, as previously set e.g.
Definition: User.php:5307
Block\prevents
prevents( $action, $x=null)
Get/set whether the Block prevents a given action.
Definition: Block.php:1013
User\passwordChangeInputAttribs
static passwordChangeInputAttribs()
Provide an array of HTML5 attributes to put on an input element intended for the user to enter a new ...
Definition: User.php:5404
Preferences\getPreferences
static getPreferences( $user, IContextSource $context)
Definition: Preferences.php:82
User\updateNewtalk
updateNewtalk( $field, $id, $curRev=null)
Add or update the new messages flag.
Definition: User.php:2374
User\setCookie
setCookie( $name, $value, $exp=0, $secure=null, $params=[], $request=null)
Set a cookie on the user's client.
Definition: User.php:3838
User\loadFromId
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
Definition: User.php:430
User\load
load( $flags=self::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition: User.php:367
User\getNewtalk
getNewtalk()
Check if the user has new messages.
Definition: User.php:2269
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:45
User\$mOptionsLoaded
$mOptionsLoaded
Bool Whether the cache variables have been loaded.
Definition: User.php:244
User\inDnsBlacklist
inDnsBlacklist( $ip, $bases)
Whether the given IP is in a given DNS blacklist.
Definition: User.php:1799
$wgUser
$wgUser
Definition: Setup.php:781
$context
error also a ContextSource you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2612
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:579
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:265
User\confirmationTokenUrl
confirmationTokenUrl( $token)
Return a URL the user can use to confirm their email address.
Definition: User.php:4526
file
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing we can concentrate it all in an extension file
Definition: hooks.txt:93
$request
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2612
User\clearSharedCache
clearSharedCache( $mode='changed')
Clear user data from memcached.
Definition: User.php:2472
wfCanIPUseHTTPS
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
Definition: GlobalFunctions.php:3564
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
User\$mToken
string $mToken
Definition: User.php:218
ObjectCache\getLocalClusterInstance
static getLocalClusterInstance()
Get the main cluster-local cache object.
Definition: ObjectCache.php:357
User\isValidPassword
isValidPassword( $password)
Is the input a valid password for this user?
Definition: User.php:989
User\getId
getId()
Get the user's ID.
Definition: User.php:2200
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:116
User\makeGroupLinkWiki
static makeGroupLinkWiki( $group, $text='')
Create a link to the group in Wikitext, if available; else return the group name.
Definition: User.php:4945
User\useFilePatrol
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition: User.php:3574
$wgMaxArticleSize
$wgMaxArticleSize
Maximum article size in kilobytes.
Definition: DefaultSettings.php:2174
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:3485
User\$mBlock
Block $mBlock
Definition: User.php:296
Block\clearCookie
static clearCookie(WebResponse $response)
Unset the 'BlockID' cookie.
Definition: Block.php:1479
User\$mBlockreason
string $mBlockreason
Definition: User.php:276
User\getTokenUrl
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
Definition: User.php:4553
User\$mImplicitGroups
array $mImplicitGroups
Definition: User.php:280
User\isLocallyBlockedProxy
static isLocallyBlockedProxy( $ip)
Check if an IP address is in the local proxy list.
Definition: User.php:1845
User\resetTokenFromOption
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition: User.php:2993
User\loadFromUserObject
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition: User.php:1398
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5467
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:4366
IP\isInRanges
static isInRanges( $ip, $ranges)
Determines if an IP address is a list of CIDR a.b.c.d/n ranges.
Definition: IP.php:656
$opt
$opt
Definition: postprocess-phan.php:115
User\isBot
isBot()
Definition: User.php:3493
Block\newFromID
static newFromID( $id)
Load a blocked user from their block id.
Definition: Block.php:184
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:2451
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:3376
User\spreadBlock
spreadBlock()
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition: User.php:4229
AutoCommitUpdate
Deferrable Update for closure/callback updates that should use auto-commit mode.
Definition: AutoCommitUpdate.php:9
User\incEditCount
incEditCount()
Deferred version of incEditCountImmediate()
Definition: User.php:5081
User\newFromSession
static newFromSession(WebRequest $request=null)
Create a new user object using data from session.
Definition: User.php:622
captcha-old.count
count
Definition: captcha-old.py:225
wfGetLB
wfGetLB( $wiki=false)
Get a load balancer object.
Definition: GlobalFunctions.php:3073
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:114
$wgDefaultUserOptions
if( $wgRCFilterByAge) $wgDefaultUserOptions['rcdays']
Definition: Setup.php:284
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:3050
Block\chooseBlock
static chooseBlock(array $blocks, array $ipChain)
From a list of multiple blocks, find the most exact and strongest Block.
Definition: Block.php:1234
User\getBlock
getBlock( $bFromSlave=true)
Get the block affecting the user, or null if the user is not blocked.
Definition: User.php:2053
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
User\isEmailConfirmationPending
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
Definition: User.php:4665
MediaWiki\Logger\LoggerFactory\getInstance
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Definition: LoggerFactory.php:93
User\getBlockId
getBlockId()
If user is blocked, return the ID for the block.
Definition: User.php:2104
User\getIntOption
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition: User.php:2928
User\getOptions
getOptions( $flags=0)
Get all user's options.
Definition: User.php:2885
UserGroupMembership\getGroupName
static getGroupName( $group)
Gets the localized friendly name for a group, if it exists.
Definition: UserGroupMembership.php:405
User\$mTouched
string $mTouched
TS_MW timestamp from the DB.
Definition: User.php:214
User\__construct
__construct()
Lightweight constructor for an anonymous user.
Definition: User.php:325
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1954
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1994
User\getToken
getToken( $forceCreation=true)
Get the user's current token.
Definition: User.php:2672
User\spreadAnyEditBlock
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition: User.php:4216
User\getNewMessageRevisionId
getNewMessageRevisionId()
Get the revision ID for the last talk page revision viewed by the talk page owner.
Definition: User.php:2332
User\loadDefaults
loadDefaults( $name=false)
Set cached properties to default.
Definition: User.php:1146
User\$mAllowUsertalk
bool $mAllowUsertalk
Definition: User.php:299
$status
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1049
$wgEmailAuthentication
$wgEmailAuthentication
Require email authentication before sending mail to an email address.
Definition: DefaultSettings.php:1677
User\$mOptions
array $mOptions
Definition: User.php:290
User\$mNewtalk
$mNewtalk
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition: User.php:266
User\loadOptions
loadOptions( $data=null)
Load the user options either from cache, the database or an array.
Definition: User.php:5234
User\makeGroupLinkHTML
static makeGroupLinkHTML( $group, $text='')
Create a link to the group in HTML, if available; else return the group name.
Definition: User.php:4921
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:246
$req
this hook is for auditing only $req
Definition: hooks.txt:990
User\setNewpassword
setNewpassword( $str, $throttle=true)
Set the password for a password reminder or new account email.
Definition: User.php:2730
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Definition: DeferredUpdates.php:76
Autopromote\getAutopromoteGroups
static getAutopromoteGroups(User $user)
Get the groups for the given user based on $wgAutopromote.
Definition: Autopromote.php:35
StatusValue\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: StatusValue.php:63
User\setEmailWithConfirmation
setEmailWithConfirmation( $str)
Set the user's e-mail address and a confirmation mail if needed.
Definition: User.php:2775
$wgEnableUserEmail
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
Definition: DefaultSettings.php:1589
User\$mHideName
bool $mHideName
Definition: User.php:288
User\getStubThreshold
getStubThreshold()
Get the user preferred stub threshold.
Definition: User.php:3218
$params
$params
Definition: styleTest.css.php:40
Block\newFromTarget
static newFromTarget( $specificTarget, $vagueTarget=null, $fromMaster=false)
Given a target and the target's type, get an existing Block object if possible.
Definition: Block.php:1113
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1277
User\$mLocked
bool $mLocked
Definition: User.php:286
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:556
User\loadFromRow
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition: User.php:1302
User\setEmailAuthenticationTimestamp
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition: User.php:4597
IP\isIPv6
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
Definition: IP.php:90
User\$mEmail
string $mEmail
Definition: User.php:212
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:4288
User\$mOptionOverrides
array $mOptionOverrides
Definition: User.php:237
User\getGroups
getGroups()
Get the list of explicit group memberships this user has.
Definition: User.php:3274
$s
$s
Definition: mergeMessageFileList.php:188
User\getBlockFromCookieValue
getBlockFromCookieValue( $blockCookieVal)
Try to load a Block from an ID given in a cookie value.
Definition: User.php:1738
PasswordFactory\generateRandomPasswordString
static generateRandomPasswordString( $minLength=10)
Generate a random string suitable for a password.
Definition: PasswordFactory.php:198
MWCryptRand\generateHex
static generateHex( $chars, $forceStrong=false)
Generate a run of (ideally) cryptographically random data and return it in hexadecimal string format.
Definition: MWCryptRand.php:76
User\useNPPatrol
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition: User.php:3562
$res
$res
Definition: database.txt:21
DBAccessObjectUtils\getDBOptions
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
Definition: DBAccessObjectUtils.php:52
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:3179
User\isSafeToLoad
isSafeToLoad()
Test if it's safe to load this User object.
Definition: User.php:350
ContextSource\getRequest
getRequest()
Get the WebRequest object.
Definition: ContextSource.php:78
User\getEditTokenTimestamp
static getEditTokenTimestamp( $val)
Get the embedded timestamp from a token.
Definition: User.php:4400
User\setEmail
setEmail( $str)
Set the user's e-mail address.
Definition: User.php:2758
User\idForName
idForName( $flags=0)
If only this user's username is known, and it exists, return the user ID.
Definition: User.php:4046
$success
$success
Definition: NoLocalSettings.php:44
User\isValidUserName
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:835
User
User
Definition: All_system_messages.txt:425
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:4443
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4766
User\getEmailAuthenticationTimestamp
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition: User.php:2748
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:1901
$type
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition: hooks.txt:2536
User\$mFrom
$mFrom
String Initialization data source if mLoadedItems!==true.
Definition: User.php:261
$lbFactory
$lbFactory
Definition: doMaintenance.php:117
User\initEditCount
initEditCount( $add=0)
Initialize user_editcount from data out of the revision table.
Definition: User.php:5142
IDBAccessObject
Interface for database access objects.
Definition: IDBAccessObject.php:55
User\useRCPatrol
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition: User.php:3553
$base
$base
Definition: generateLocalAutoload.php:10
User\invalidateEmail
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
Definition: User.php:4583
$messages
$messages
Definition: LogTests.i18n.php:8
UserGroupMembership\getGroupPage
static getGroupPage( $group)
Gets the title of a page describing a particular user group.
Definition: UserGroupMembership.php:430
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:643
User\loadGroups
loadGroups()
Load the groups from the database if they aren't already loaded.
Definition: User.php:1408
User\$mHash
string $mHash
Definition: User.php:272
MediaWiki\Session\Token\getTimestamp
static getTimestamp( $token)
Decode the timestamp from a token string.
Definition: Token.php:61
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1092
User\deleteNewtalk
deleteNewtalk( $field, $id)
Clear the new messages flag for the given user.
Definition: User.php:2399
MailAddress\newFromUser
static newFromUser(User $user)
Create a new MailAddress object for the given user.
Definition: MailAddress.php:75
User\equals
equals(User $user)
Checks if two user objects point to the same user.
Definition: User.php:5511
User\$mCacheVars
static $mCacheVars
Array of Strings List of member variables which are saved to the shared cache (memcached).
Definition: User.php:95
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
pages
The ContentHandler facility adds support for arbitrary content types on wiki pages
Definition: contenthandler.txt:1
User\getRights
getRights()
Get the permissions this user has.
Definition: User.php:3233
$wgClockSkewFudge
$wgClockSkewFudge
Clock skew or the one-second resolution of time() can occasionally cause cache problems when the user...
Definition: DefaultSettings.php:2584
User\createNew
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition: User.php:4081
User\getRequest
getRequest()
Get the WebRequest object to use with this object.
Definition: User.php:3587
Preferences\getSaveBlacklist
static getSaveBlacklist()
Definition: Preferences.php:72
User\getAutomaticGroups
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:3323
User\INVALID_TOKEN
const INVALID_TOKEN
@const string An invalid value for user_token
Definition: User.php:59
User\setPassword
setPassword( $str)
Set the password and reset the random token.
Definition: User.php:2583
User\$mGroupMemberships
array $mGroupMemberships
Associative array of (group name => UserGroupMembership object)
Definition: User.php:235
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1563
User\getInstanceForUpdate
getInstanceForUpdate()
Get a new instance of this user that was loaded from the master via a locking read.
Definition: User.php:5491
User\$mGroups
array $mGroups
No longer used since 1.29; use User::getGroups() instead.
Definition: User.php:233
IDBAccessObject\READ_LOCKING
const READ_LOCKING
Constants for object loading bitfield flags (higher => higher QoS)
Definition: IDBAccessObject.php:62
User\isBlockedFrom
isBlockedFrom( $title, $bFromSlave=false)
Check if user is blocked from editing a particular article.
Definition: User.php:2065
User\newSystemUser
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition: User.php:684
UserGroupMembership\getMembershipsForUser
static getMembershipsForUser( $userId, IDatabase $db=null)
Returns UserGroupMembership objects for all the groups a user currently belongs to.
Definition: UserGroupMembership.php:281
NS_MAIN
const NS_MAIN
Definition: Defines.php:62
User\addGroup
addGroup( $group, $expiry=null)
Add the user to the given group.
Definition: User.php:3411
MailAddress
Stores a single person's name and email address.
Definition: MailAddress.php:32
LoggedOutEditToken
Value object representing a logged-out user's edit token.
Definition: LoggedOutEditToken.php:35
User\matchEditToken
matchEditToken( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session.
Definition: User.php:4417
User\getEmail
getEmail()
Get the user's e-mail address.
Definition: User.php:2738
User\$mRights
array $mRights
Definition: User.php:274
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, IDatabase $dbw=null)
Add a callable update.
Definition: DeferredUpdates.php:111
article
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an article
Definition: design.txt:25
User\getTalkPage
getTalkPage()
Get this user's talk page title.
Definition: User.php:4297
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:4152
User\makeUpdateConditions
makeUpdateConditions(Database $db, array $conditions)
Builds update conditions.
Definition: User.php:1482
User\invalidateCache
invalidateCache()
Immediately touch the user data cache for this account.
Definition: User.php:2496
User\setInternalPassword
setInternalPassword( $str)
Set the password and reset the random token unconditionally.
Definition: User.php:2595
MWException
MediaWiki exception.
Definition: MWException.php:26
User\invalidationTokenUrl
invalidationTokenUrl( $token)
Return a URL the user can use to invalidate their email address.
Definition: User.php:4535
wfMemcKey
wfMemcKey()
Make a cache key for the local wiki.
Definition: GlobalFunctions.php:2961
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
User\isLocked
isLocked()
Check if user account is locked.
Definition: User.php:2164
User\$mDatePreference
string $mDatePreference
Definition: User.php:268
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1128
$property
$property
Definition: styleTest.css.php:44
User\checkPasswordValidity
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition: User.php:1036
Skin\normalizeKey
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition: Skin.php:93
User\confirmEmail
confirmEmail()
Mark the e-mail address confirmed.
Definition: User.php:4566
User\setItemLoaded
setItemLoaded( $item)
Set that an item has been loaded.
Definition: User.php:1194
User\getPasswordFactory
static getPasswordFactory()
Lazily instantiate and return a factory object for making passwords.
Definition: User.php:5373
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:346
User\blockedFor
blockedFor()
If user is blocked, return the specified reason for the block.
Definition: User.php:2095
boolean
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a boolean(which is limited to accepting true, false, 0, 1, t, or f) *The default data type for all VARCHAR
User\setNewtalk
setNewtalk( $val, $curRev=null)
Update the 'You have new messages!' status.
Definition: User.php:2419
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
$page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2536
User\$mBlockedFromCreateAccount
Block $mBlockedFromCreateAccount
Definition: User.php:302
in
null for the wiki Added in
Definition: hooks.txt:1572
User\$mGlobalBlock
Block $mGlobalBlock
Definition: User.php:284
User\isAllowedToCreateAccount
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition: User.php:4279
MediaWiki\Auth\AuthenticationResponse
This is a value object to hold authentication response data.
Definition: AuthenticationResponse.php:37
User\logout
logout()
Log this user out.
Definition: User.php:3936
User\confirmationToken
confirmationToken(&$expiration)
Generate, store, and return a new e-mail confirmation code.
Definition: User.php:4508
User\getCacheKey
getCacheKey(WANObjectCache $cache)
Definition: User.php:471
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:2010
User\getImplicitGroups
static getImplicitGroups()
Get a list of implicit groups.
Definition: User.php:4889
User\isHidden
isHidden()
Check if user account is hidden.
Definition: User.php:2181
User\randomPassword
static randomPassword()
Return a random password.
Definition: User.php:1133
User\$mBlockedby
string $mBlockedby
Definition: User.php:270
UserGroupMembership\newFromRow
static newFromRow( $row)
Creates a new UserGroupMembership object from a database row.
Definition: UserGroupMembership.php:92
IP\getSubnet
static getSubnet( $ip)
Returns the subnet of a given IP.
Definition: IP.php:730
$limit
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers please use GetContentModels hook to make them known to core if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1049
User\isBlockedFromEmailuser
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition: User.php:4270
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:819
User\validateCache
validateCache( $timestamp)
Validate the cache for this account.
Definition: User.php:2527
User\getEffectiveGroups
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:3300
User\isPingLimitable
isPingLimitable()
Is this user subject to rate limiting?
Definition: User.php:1876
User\removeGroup
removeGroup( $group)
Remove the user from the given group.
Definition: User.php:3447
User\canReceiveEmail
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition: User.php:4625
User\isNewbie
isNewbie()
Determine whether the user is a newbie.
Definition: User.php:4307
User\isAllowedAll
isAllowedAll()
Definition: User.php:3525
$wgLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition: design.txt:56
User\touch
touch()
Update the "touched" timestamp for the user.
Definition: User.php:2513
User\clearInstanceCache
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition: User.php:1538
$wgEnableEmail
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
Definition: DefaultSettings.php:1583
User\CHECK_USER_RIGHTS
const CHECK_USER_RIGHTS
Definition: User.php:82
User\TOKEN_LENGTH
const TOKEN_LENGTH
@const int Number of characters in user_token field.
Definition: User.php:54
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1769
User\clearCookie
clearCookie( $name, $secure=null, $params=[])
Clear a cookie on the user's client.
Definition: User.php:3859
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:514
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2023
User\addWatch
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
Definition: User.php:3618
User\$mQuickTouched
string $mQuickTouched
TS_MW timestamp from cache.
Definition: User.php:216
User\setName
setName( $str)
Set the user name.
Definition: User.php:2252
DB_MASTER
const DB_MASTER
Definition: defines.php:26
User\$mRealName
string $mRealName
Definition: User.php:209
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:3134
UserArray\newFromIDs
static newFromIDs( $ids)
Definition: UserArray.php:45
UserPasswordPolicy
Check if a user's password complies with any password policies that apply to that user,...
Definition: UserPasswordPolicy.php:28
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:999
User\$mLoadedItems
$mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition: User.php:249
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
User\clearNotification
clearNotification(&$title, $oldid=0)
Clear the user's notification timestamp for the given title.
Definition: User.php:3652
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:3984
User\addNewUserLogEntry
addNewUserLogEntry( $action=false, $reason='')
Add a newuser log entry for this user.
Definition: User.php:5211
Block\getBlocksForIPList
static getBlocksForIPList(array $ipChain, $isAnon, $fromMaster=false)
Get all blocks that match any IP from an array of IP addresses.
Definition: Block.php:1153
User\$mCoreRights
static $mCoreRights
Array of Strings Core rights.
Definition: User.php:120
$wgFullyInitialised
foreach( $wgExtensionFunctions as $func) if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) if(! $wgCommandLineMode) $wgFullyInitialised
Definition: Setup.php:856
User\getFirstEditTimestamp
getFirstEditTimestamp()
Get the timestamp of the first edit.
Definition: User.php:4694
User\GETOPTIONS_EXCLUDE_DEFAULTS
const GETOPTIONS_EXCLUDE_DEFAULTS
Exclude user options that are set to their default value.
Definition: User.php:77
User\setRealName
setRealName( $str)
Set the user's real name.
Definition: User.php:2842
User\EDIT_TOKEN_SUFFIX
const EDIT_TOKEN_SUFFIX
Global constant made accessible as class constants so that autoloader magic can be used.
Definition: User.php:66
User\getNewMessageLinks
getNewMessageLinks()
Return the data needed to construct links for new talk page message alerts.
Definition: User.php:2307
User\setExtendedLoginCookie
setExtendedLoginCookie( $name, $value, $secure)
Set an extended login cookie on the user's client.
Definition: User.php:3879
any
they could even be mouse clicks or menu items whatever suits your program You should also get your if any
Definition: COPYING.txt:326
User\getBlockedStatus
getBlockedStatus( $bFromSlave=true)
Get blocking information.
Definition: User.php:1618
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:538
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3011
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:65
User\getFormerGroups
getFormerGroups()
Returns the groups the user has belonged to.
Definition: User.php:3352
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:739
Block\getIdFromCookieValue
static getIdFromCookieValue( $cookieValue)
Get the stored ID from the 'BlockID' cookie.
Definition: Block.php:1508
$value
$value
Definition: styleTest.css.php:45
DBAccessObjectUtils\hasFlags
static hasFlags( $bitfield, $flags)
Definition: DBAccessObjectUtils.php:35
User\incEditCountImmediate
incEditCountImmediate()
Increment the user's edit-count field.
Definition: User.php:5095
User\loadFromSession
loadFromSession()
Load user data from the session.
Definition: User.php:1205
User\isBlockedGlobally
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2117
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:2857
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:76
User\isDnsBlacklisted
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
Definition: User.php:1778
User\getTouched
getTouched()
Get the user touched timestamp.
Definition: User.php:2539
User\$mId
int $mId
Cache variables.
Definition: User.php:205
User\getGlobalBlock
getGlobalBlock( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2131
User\__toString
__toString()
Definition: User.php:332
MediaWiki\Session\SessionManager
This serves as the entry point to the MediaWiki session handling system.
Definition: SessionManager.php:49
Title\GAID_FOR_UPDATE
const GAID_FOR_UPDATE
Used to be GAID_FOR_UPDATE define.
Definition: Title.php:54
User\checkAndSetTouched
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
Definition: User.php:1500
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:81
User\getRealName
getRealName()
Get the user's real name.
Definition: User.php:2830
User\$idCacheByName
static $idCacheByName
Definition: User.php:313
Linker\link
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:107
User\setCookies
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
Definition: User.php:3900
User\getGroupPermissions
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
Definition: User.php:4716
User\getGroupPage
static getGroupPage( $group)
Get the title of a page describing a particular group.
Definition: User.php:4906
User\changeableGroups
changeableGroups()
Returns an array of groups that this user can add and remove.
Definition: User.php:5042
User\clearAllNotifications
clearAllNotifications()
Resets all of the given user's page-change notification timestamps.
Definition: User.php:3720
User\VERSION
const VERSION
@const int Serialized record version.
Definition: User.php:71
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1956
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:4431
User\checkPassword
checkPassword( $password)
Check to see if the given clear-text password is one of the accepted passwords.
Definition: User.php:4317
User\getAllGroups
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4860
User\removeWatch
removeWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Stop watching an article.
Definition: User.php:3635
User\getAllRights
static getAllRights()
Get a list of all available permissions.
Definition: User.php:4872
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:468
User\blockedBy
blockedBy()
If user is blocked, return the name of the user who placed the block.
Definition: User.php:2086
User\getDBTouched
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition: User.php:2561
Wikimedia\Rdbms\Database\timestamp
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
Definition: Database.php:2904
User\getGroupMember
static getGroupMember( $group, $username='#')
Get the localized descriptive name for a member of a group, if it exists.
Definition: User.php:4849
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:82
IP\isIPv4
static isIPv4( $ip)
Given a string, determine if it as valid IP in IPv4 only.
Definition: IP.php:101
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:38
User\changeableByGroup
static changeableByGroup( $group)
Returns an array of the groups that a particular group can add/remove.
Definition: User.php:4969
Block\TYPE_USER
const TYPE_USER
Definition: Block.php:83
User\$blockTrigger
string $blockTrigger
Indicates type of block (used for eventlogging) Permitted values: 'cookie-block', 'proxy-block',...
Definition: User.php:311
User\getGroupName
static getGroupName( $group)
Get the localized descriptive name for a group, if it exists.
Definition: User.php:4836
$wgUseEnotif
$wgUseEnotif
Definition: Setup.php:345
Autopromote\getAutopromoteOnceGroups
static getAutopromoteOnceGroups(User $user, $event)
Get the groups for the given user based on the given criteria.
Definition: Autopromote.php:63
User\setId
setId( $v)
Set the user and reload all fields according to a given ID.
Definition: User.php:2216
User\getExperienceLevel
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition: User.php:3791
page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk page
Definition: hooks.txt:2536
$wgUserEmailConfirmationTokenExpiry
$wgUserEmailConfirmationTokenExpiry
The time, in seconds, when an email confirmation email expires.
Definition: DefaultSettings.php:1616
User\getRegistration
getRegistration()
Get the timestamp of account creation.
Definition: User.php:4680
PasswordFactory\newInvalidPassword
static newInvalidPassword()
Create an InvalidPassword.
Definition: PasswordFactory.php:214
IP\sanitizeIP
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
Definition: IP.php:140
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:4786
and
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two and(2) offer you this license which gives you legal permission to copy
User\isAllowedAny
isAllowedAny()
Check if user is allowed to access a feature / make an action.
Definition: User.php:3510
User\getRightDescription
static getRightDescription( $right)
Get the description of a given right.
Definition: User.php:5172
User\changeAuthenticationData
changeAuthenticationData(array $data)
Changes credentials of the user.
Definition: User.php:2645
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:370
User\isLoggedIn
isLoggedIn()
Get whether the user is logged in.
Definition: User.php:3477
User\checkTemporaryPassword
checkTemporaryPassword( $plaintext)
Check if the given clear-text password matches the temporary password sent by e-mail for password res...
Definition: User.php:4350
Wikimedia\Rdbms\DBExpectedError
Base class for the more common types of database errors.
Definition: DBExpectedError.php:35
User\getTitleKey
getTitleKey()
Get the user's name escaped by underscores.
Definition: User.php:2261
$code
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition: hooks.txt:783
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2710
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:759
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:799
User\$mEmailTokenExpires
string $mEmailTokenExpires
Definition: User.php:224
User\findUsersByGroup
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition: User.php:919
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:1076
User\isEmailConfirmed
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition: User.php:4639
$rev
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1741
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
User\getGrantName
static getGrantName( $grant)
Get the name of a given grant.
Definition: User.php:5185
Block
Definition: Block.php:27
UserCache\singleton
static singleton()
Definition: UserCache.php:34
Revision\loadFromTimestamp
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition: Revision.php:307
$keys
$keys
Definition: testCompression.php:65
NS_USER
const NS_USER
Definition: Defines.php:64
User\loadFromCache
loadFromCache()
Load user data from shared cache, given mId has already been set.
Definition: User.php:492
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1432
User\sendMail
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition: User.php:4482
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:2965
true
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1956
User\$mEmailToken
string $mEmailToken
Definition: User.php:222
ManualLogEntry
Class for creating log entries manually, to inject them into the database.
Definition: LogEntry.php:396
User\checkNewtalk
checkNewtalk( $field, $id)
Internal uncached check for new messages.
Definition: User.php:2359
User\loadFromDatabase
loadFromDatabase( $flags=self::READ_LATEST)
Load user and user_group data from the database.
Definition: User.php:1252
HTMLFormField\flattenOptions
static flattenOptions( $options)
flatten an array of options to a single array, for instance, a set of "<options>" inside "<optgroups>...
Definition: HTMLFormField.php:1126
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
User\selectFields
static selectFields()
Return the list of user fields that should be selected to create a new user object.
Definition: User.php:5444
PasswordFactory
Factory class for creating and checking Password objects.
Definition: PasswordFactory.php:28
User\$mName
string $mName
Definition: User.php:207
User\$mRegistration
string $mRegistration
Definition: User.php:226
$t
$t
Definition: testCompression.php:67
User\addNewUserLogEntryAutoCreate
addNewUserLogEntryAutoCreate()
Add an autocreate newuser log entry for this user Used by things like CentralAuth and perhaps other a...
Definition: User.php:5223
User\newFromConfirmationCode
static newFromConfirmationCode( $code, $flags=0)
Factory method to fetch whichever user has a given email confirmation code.
Definition: User.php:598
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:87
User\$mRequest
WebRequest $mRequest
Definition: User.php:293
MediaWiki\Session\Token
Value object representing a CSRF token.
Definition: Token.php:32
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:639
User\isWatched
isWatched( $title, $checkRights=self::CHECK_USER_RIGHTS)
Check the watched status of an article.
Definition: User.php:3604
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
UserGroupMembership\getMembership
static getMembership( $userId, $group, IDatabase $db=null)
Returns a UserGroupMembership object that pertains to the given user and group, or false if the user ...
Definition: UserGroupMembership.php:312
User\getBoolOption
getBoolOption( $oname)
Get the user's current setting for a given option, as a boolean value.
Definition: User.php:2916
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:884
$incrBy
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled $incrBy
Definition: hooks.txt:2536
User\isBlocked
isBlocked( $bFromSlave=true)
Check if user is blocked.
Definition: User.php:2043
User\$mFormerGroups
array $mFormerGroups
Definition: User.php:282
$wgPasswordSender
$wgPasswordSender
Sender email address for e-mail notifications.
Definition: DefaultSettings.php:1562
User\isBlockedFromCreateAccount
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
Definition: User.php:4248
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:4390
User\requiresHTTPS
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition: User.php:3199
User\$mAllRights
static $mAllRights
String Cached results of getAllRights()
Definition: User.php:200
User\isItemLoaded
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition: User.php:1184
User\purge
static purge( $wikiId, $userId)
Definition: User.php:460
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:50
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:2944
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:783
User\getPasswordValidity
getPasswordValidity( $password)
Given unvalidated password input, return error message on failure.
Definition: User.php:1000
User\whoIsReal
static whoIsReal( $id)
Get the real name of a user given their user ID.
Definition: User.php:749
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2225
UserGroupMembership\getGroupMemberName
static getGroupMemberName( $group, $username)
Gets the localized name for a member of a group, if it exists.
Definition: UserGroupMembership.php:418
User\doLogout
doLogout()
Clear the user's session, and reset the instance cache.
Definition: User.php:3948
User\$queryFlagsUsed
integer $queryFlagsUsed
User::READ_* constant bitfield used to load data.
Definition: User.php:305
User\getGroupMemberships
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
Definition: User.php:3287
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
User\canSendEmail
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition: User.php:4608
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:959
User\getMutableCacheKeys
getMutableCacheKeys(WANObjectCache $cache)
Definition: User.php:480
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2749
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37
User\$mEffectiveGroups
array $mEffectiveGroups
Definition: User.php:278
User\setPasswordInternal
setPasswordInternal( $str)
Actually set the password and such.
Definition: User.php:2607
UserGroupMembership
Represents a "user group membership" – a specific instance of a user belonging to a group.
Definition: UserGroupMembership.php:36
IP\isIPAddress
static isIPAddress( $ip)
Determine if a string is as valid IP address or network (CIDR prefix).
Definition: IP.php:79
User\$mEditCount
int $mEditCount
Definition: User.php:228
User\$mEmailAuthenticated
string $mEmailAuthenticated
Definition: User.php:220
array
the array() calling protocol came about after MediaWiki 1.4rc1.
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4743
User\isAllowed
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3540
MWExceptionHandler\logException
static logException( $e, $catcher=self::CAUGHT_BY_OTHER)
Log an exception to the exception log (if enabled).
Definition: MWExceptionHandler.php:596
User\listOptionKinds
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
Definition: User.php:3027
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56