MediaWiki  1.30.0
User.php
Go to the documentation of this file.
1 <?php
23 use IPSet\IPSet;
30 use Wikimedia\ScopedCallback;
33 
39 define( 'EDIT_TOKEN_SUFFIX', Token::SUFFIX );
40 
51 class User implements IDBAccessObject {
55  const TOKEN_LENGTH = 32;
56 
60  const INVALID_TOKEN = '*** INVALID ***';
61 
68 
72  const VERSION = 11;
73 
79 
83  const CHECK_USER_RIGHTS = true;
84 
88  const IGNORE_USER_RIGHTS = false;
89 
96  protected static $mCacheVars = [
97  // user table
98  'mId',
99  'mName',
100  'mRealName',
101  'mEmail',
102  'mTouched',
103  'mToken',
104  'mEmailAuthenticated',
105  'mEmailToken',
106  'mEmailTokenExpires',
107  'mRegistration',
108  'mEditCount',
109  // user_groups table
110  'mGroupMemberships',
111  // user_properties table
112  'mOptionOverrides',
113  ];
114 
121  protected static $mCoreRights = [
122  'apihighlimits',
123  'applychangetags',
124  'autoconfirmed',
125  'autocreateaccount',
126  'autopatrol',
127  'bigdelete',
128  'block',
129  'blockemail',
130  'bot',
131  'browsearchive',
132  'changetags',
133  'createaccount',
134  'createpage',
135  'createtalk',
136  'delete',
137  'deletechangetags',
138  'deletedhistory',
139  'deletedtext',
140  'deletelogentry',
141  'deleterevision',
142  'edit',
143  'editcontentmodel',
144  'editinterface',
145  'editprotected',
146  'editmyoptions',
147  'editmyprivateinfo',
148  'editmyusercss',
149  'editmyuserjs',
150  'editmywatchlist',
151  'editsemiprotected',
152  'editusercss',
153  'edituserjs',
154  'hideuser',
155  'import',
156  'importupload',
157  'ipblock-exempt',
158  'managechangetags',
159  'markbotedits',
160  'mergehistory',
161  'minoredit',
162  'move',
163  'movefile',
164  'move-categorypages',
165  'move-rootuserpages',
166  'move-subpages',
167  'nominornewtalk',
168  'noratelimit',
169  'override-export-depth',
170  'pagelang',
171  'patrol',
172  'patrolmarks',
173  'protect',
174  'purge',
175  'read',
176  'reupload',
177  'reupload-own',
178  'reupload-shared',
179  'rollback',
180  'sendemail',
181  'siteadmin',
182  'suppressionlog',
183  'suppressredirect',
184  'suppressrevision',
185  'unblockself',
186  'undelete',
187  'unwatchedpages',
188  'upload',
189  'upload_by_url',
190  'userrights',
191  'userrights-interwiki',
192  'viewmyprivateinfo',
193  'viewmywatchlist',
194  'viewsuppressed',
195  'writeapi',
196  ];
197 
201  protected static $mAllRights = false;
202 
204  // @{
206  public $mId;
208  public $mName;
210  public $mRealName;
211 
213  public $mEmail;
215  public $mTouched;
217  protected $mQuickTouched;
219  protected $mToken;
223  protected $mEmailToken;
227  protected $mRegistration;
229  protected $mEditCount;
234  private $mGroups;
238  protected $mOptionOverrides;
239  // @}
240 
244  // @{
246 
250  protected $mLoadedItems = [];
251  // @}
252 
262  public $mFrom;
263 
267  protected $mNewtalk;
269  protected $mDatePreference;
271  public $mBlockedby;
273  protected $mHash;
275  public $mRights;
277  protected $mBlockreason;
279  protected $mEffectiveGroups;
281  protected $mImplicitGroups;
283  protected $mFormerGroups;
285  protected $mGlobalBlock;
287  protected $mLocked;
289  public $mHideName;
291  public $mOptions;
292 
294  private $mRequest;
295 
297  public $mBlock;
298 
300  protected $mAllowUsertalk;
301 
303  private $mBlockedFromCreateAccount = false;
304 
306  protected $queryFlagsUsed = self::READ_NORMAL;
307 
308  public static $idCacheByName = [];
309 
320  public function __construct() {
321  $this->clearInstanceCache( 'defaults' );
322  }
323 
327  public function __toString() {
328  return (string)$this->getName();
329  }
330 
345  public function isSafeToLoad() {
347 
348  // The user is safe to load if:
349  // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data)
350  // * mLoadedItems === true (already loaded)
351  // * mFrom !== 'session' (sessions not involved at all)
352 
353  return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) ||
354  $this->mLoadedItems === true || $this->mFrom !== 'session';
355  }
356 
362  public function load( $flags = self::READ_NORMAL ) {
364 
365  if ( $this->mLoadedItems === true ) {
366  return;
367  }
368 
369  // Set it now to avoid infinite recursion in accessors
370  $oldLoadedItems = $this->mLoadedItems;
371  $this->mLoadedItems = true;
372  $this->queryFlagsUsed = $flags;
373 
374  // If this is called too early, things are likely to break.
375  if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
377  ->warning( 'User::loadFromSession called before the end of Setup.php', [
378  'exception' => new Exception( 'User::loadFromSession called before the end of Setup.php' ),
379  ] );
380  $this->loadDefaults();
381  $this->mLoadedItems = $oldLoadedItems;
382  return;
383  }
384 
385  switch ( $this->mFrom ) {
386  case 'defaults':
387  $this->loadDefaults();
388  break;
389  case 'name':
390  // Make sure this thread sees its own changes
391  if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) {
392  $flags |= self::READ_LATEST;
393  $this->queryFlagsUsed = $flags;
394  }
395 
396  $this->mId = self::idFromName( $this->mName, $flags );
397  if ( !$this->mId ) {
398  // Nonexistent user placeholder object
399  $this->loadDefaults( $this->mName );
400  } else {
401  $this->loadFromId( $flags );
402  }
403  break;
404  case 'id':
405  $this->loadFromId( $flags );
406  break;
407  case 'session':
408  if ( !$this->loadFromSession() ) {
409  // Loading from session failed. Load defaults.
410  $this->loadDefaults();
411  }
412  Hooks::run( 'UserLoadAfterLoadFromSession', [ $this ] );
413  break;
414  default:
415  throw new UnexpectedValueException(
416  "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
417  }
418  }
419 
425  public function loadFromId( $flags = self::READ_NORMAL ) {
426  if ( $this->mId == 0 ) {
427  // Anonymous users are not in the database (don't need cache)
428  $this->loadDefaults();
429  return false;
430  }
431 
432  // Try cache (unless this needs data from the master DB).
433  // NOTE: if this thread called saveSettings(), the cache was cleared.
434  $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
435  if ( $latest ) {
436  if ( !$this->loadFromDatabase( $flags ) ) {
437  // Can't load from ID
438  return false;
439  }
440  } else {
441  $this->loadFromCache();
442  }
443 
444  $this->mLoadedItems = true;
445  $this->queryFlagsUsed = $flags;
446 
447  return true;
448  }
449 
455  public static function purge( $wikiId, $userId ) {
457  $key = $cache->makeGlobalKey( 'user', 'id', $wikiId, $userId );
458  $cache->delete( $key );
459  }
460 
466  protected function getCacheKey( WANObjectCache $cache ) {
467  return $cache->makeGlobalKey( 'user', 'id', wfWikiID(), $this->mId );
468  }
469 
476  $id = $this->getId();
477 
478  return $id ? [ $this->getCacheKey( $cache ) ] : [];
479  }
480 
487  protected function loadFromCache() {
489  $data = $cache->getWithSetCallback(
490  $this->getCacheKey( $cache ),
491  $cache::TTL_HOUR,
492  function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
493  $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
494  wfDebug( "User: cache miss for user {$this->mId}\n" );
495 
496  $this->loadFromDatabase( self::READ_NORMAL );
497  $this->loadGroups();
498  $this->loadOptions();
499 
500  $data = [];
501  foreach ( self::$mCacheVars as $name ) {
502  $data[$name] = $this->$name;
503  }
504 
505  $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
506 
507  // if a user group membership is about to expire, the cache needs to
508  // expire at that time (T163691)
509  foreach ( $this->mGroupMemberships as $ugm ) {
510  if ( $ugm->getExpiry() ) {
511  $secondsUntilExpiry = wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
512  if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
513  $ttl = $secondsUntilExpiry;
514  }
515  }
516  }
517 
518  return $data;
519  },
520  [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ]
521  );
522 
523  // Restore from cache
524  foreach ( self::$mCacheVars as $name ) {
525  $this->$name = $data[$name];
526  }
527 
528  return true;
529  }
530 
532  // @{
533 
550  public static function newFromName( $name, $validate = 'valid' ) {
551  if ( $validate === true ) {
552  $validate = 'valid';
553  }
554  $name = self::getCanonicalName( $name, $validate );
555  if ( $name === false ) {
556  return false;
557  } else {
558  // Create unloaded user object
559  $u = new User;
560  $u->mName = $name;
561  $u->mFrom = 'name';
562  $u->setItemLoaded( 'name' );
563  return $u;
564  }
565  }
566 
573  public static function newFromId( $id ) {
574  $u = new User;
575  $u->mId = $id;
576  $u->mFrom = 'id';
577  $u->setItemLoaded( 'id' );
578  return $u;
579  }
580 
592  public static function newFromConfirmationCode( $code, $flags = 0 ) {
593  $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
594  ? wfGetDB( DB_MASTER )
595  : wfGetDB( DB_REPLICA );
596 
597  $id = $db->selectField(
598  'user',
599  'user_id',
600  [
601  'user_email_token' => md5( $code ),
602  'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
603  ]
604  );
605 
606  return $id ? self::newFromId( $id ) : null;
607  }
608 
616  public static function newFromSession( WebRequest $request = null ) {
617  $user = new User;
618  $user->mFrom = 'session';
619  $user->mRequest = $request;
620  return $user;
621  }
622 
637  public static function newFromRow( $row, $data = null ) {
638  $user = new User;
639  $user->loadFromRow( $row, $data );
640  return $user;
641  }
642 
678  public static function newSystemUser( $name, $options = [] ) {
679  $options += [
680  'validate' => 'valid',
681  'create' => true,
682  'steal' => false,
683  ];
684 
685  $name = self::getCanonicalName( $name, $options['validate'] );
686  if ( $name === false ) {
687  return null;
688  }
689 
690  $dbr = wfGetDB( DB_REPLICA );
691  $row = $dbr->selectRow(
692  'user',
693  self::selectFields(),
694  [ 'user_name' => $name ],
695  __METHOD__
696  );
697  if ( !$row ) {
698  // Try the master database...
699  $dbw = wfGetDB( DB_MASTER );
700  $row = $dbw->selectRow(
701  'user',
702  self::selectFields(),
703  [ 'user_name' => $name ],
704  __METHOD__
705  );
706  }
707 
708  if ( !$row ) {
709  // No user. Create it?
710  return $options['create']
711  ? self::createNew( $name, [ 'token' => self::INVALID_TOKEN ] )
712  : null;
713  }
714 
715  $user = self::newFromRow( $row );
716 
717  // A user is considered to exist as a non-system user if it can
718  // authenticate, or has an email set, or has a non-invalid token.
719  if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN ||
720  AuthManager::singleton()->userCanAuthenticate( $name )
721  ) {
722  // User exists. Steal it?
723  if ( !$options['steal'] ) {
724  return null;
725  }
726 
727  AuthManager::singleton()->revokeAccessForUser( $name );
728 
729  $user->invalidateEmail();
730  $user->mToken = self::INVALID_TOKEN;
731  $user->saveSettings();
732  SessionManager::singleton()->preventSessionsForUser( $user->getName() );
733  }
734 
735  return $user;
736  }
737 
738  // @}
739 
745  public static function whoIs( $id ) {
746  return UserCache::singleton()->getProp( $id, 'name' );
747  }
748 
755  public static function whoIsReal( $id ) {
756  return UserCache::singleton()->getProp( $id, 'real_name' );
757  }
758 
765  public static function idFromName( $name, $flags = self::READ_NORMAL ) {
767  if ( is_null( $nt ) ) {
768  // Illegal name
769  return null;
770  }
771 
772  if ( !( $flags & self::READ_LATEST ) && isset( self::$idCacheByName[$name] ) ) {
773  return self::$idCacheByName[$name];
774  }
775 
777  $db = wfGetDB( $index );
778 
779  $s = $db->selectRow(
780  'user',
781  [ 'user_id' ],
782  [ 'user_name' => $nt->getText() ],
783  __METHOD__,
784  $options
785  );
786 
787  if ( $s === false ) {
788  $result = null;
789  } else {
790  $result = $s->user_id;
791  }
792 
793  self::$idCacheByName[$name] = $result;
794 
795  if ( count( self::$idCacheByName ) > 1000 ) {
796  self::$idCacheByName = [];
797  }
798 
799  return $result;
800  }
801 
805  public static function resetIdByNameCache() {
806  self::$idCacheByName = [];
807  }
808 
825  public static function isIP( $name ) {
826  return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name )
827  || IP::isIPv6( $name );
828  }
829 
836  public function isIPRange() {
837  return IP::isValidRange( $this->mName );
838  }
839 
851  public static function isValidUserName( $name ) {
853 
854  if ( $name == ''
855  || self::isIP( $name )
856  || strpos( $name, '/' ) !== false
857  || strlen( $name ) > $wgMaxNameChars
858  || $name != $wgContLang->ucfirst( $name )
859  ) {
860  return false;
861  }
862 
863  // Ensure that the name can't be misresolved as a different title,
864  // such as with extra namespace keys at the start.
865  $parsed = Title::newFromText( $name );
866  if ( is_null( $parsed )
867  || $parsed->getNamespace()
868  || strcmp( $name, $parsed->getPrefixedText() ) ) {
869  return false;
870  }
871 
872  // Check an additional blacklist of troublemaker characters.
873  // Should these be merged into the title char list?
874  $unicodeBlacklist = '/[' .
875  '\x{0080}-\x{009f}' . # iso-8859-1 control chars
876  '\x{00a0}' . # non-breaking space
877  '\x{2000}-\x{200f}' . # various whitespace
878  '\x{2028}-\x{202f}' . # breaks and control chars
879  '\x{3000}' . # ideographic space
880  '\x{e000}-\x{f8ff}' . # private use
881  ']/u';
882  if ( preg_match( $unicodeBlacklist, $name ) ) {
883  return false;
884  }
885 
886  return true;
887  }
888 
900  public static function isUsableName( $name ) {
902  // Must be a valid username, obviously ;)
903  if ( !self::isValidUserName( $name ) ) {
904  return false;
905  }
906 
907  static $reservedUsernames = false;
908  if ( !$reservedUsernames ) {
909  $reservedUsernames = $wgReservedUsernames;
910  Hooks::run( 'UserGetReservedNames', [ &$reservedUsernames ] );
911  }
912 
913  // Certain names may be reserved for batch processes.
914  foreach ( $reservedUsernames as $reserved ) {
915  if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
916  $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
917  }
918  if ( $reserved == $name ) {
919  return false;
920  }
921  }
922  return true;
923  }
924 
935  public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
936  if ( $groups === [] ) {
937  return UserArrayFromResult::newFromIDs( [] );
938  }
939 
940  $groups = array_unique( (array)$groups );
941  $limit = min( 5000, $limit );
942 
943  $conds = [ 'ug_group' => $groups ];
944  if ( $after !== null ) {
945  $conds[] = 'ug_user > ' . (int)$after;
946  }
947 
948  $dbr = wfGetDB( DB_REPLICA );
949  $ids = $dbr->selectFieldValues(
950  'user_groups',
951  'ug_user',
952  $conds,
953  __METHOD__,
954  [
955  'DISTINCT' => true,
956  'ORDER BY' => 'ug_user',
957  'LIMIT' => $limit,
958  ]
959  ) ?: [];
960  return UserArray::newFromIDs( $ids );
961  }
962 
975  public static function isCreatableName( $name ) {
977 
978  // Ensure that the username isn't longer than 235 bytes, so that
979  // (at least for the builtin skins) user javascript and css files
980  // will work. (T25080)
981  if ( strlen( $name ) > 235 ) {
982  wfDebugLog( 'username', __METHOD__ .
983  ": '$name' invalid due to length" );
984  return false;
985  }
986 
987  // Preg yells if you try to give it an empty string
988  if ( $wgInvalidUsernameCharacters !== '' ) {
989  if ( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) {
990  wfDebugLog( 'username', __METHOD__ .
991  ": '$name' invalid due to wgInvalidUsernameCharacters" );
992  return false;
993  }
994  }
995 
996  return self::isUsableName( $name );
997  }
998 
1005  public function isValidPassword( $password ) {
1006  // simple boolean wrapper for getPasswordValidity
1007  return $this->getPasswordValidity( $password ) === true;
1008  }
1009 
1016  public function getPasswordValidity( $password ) {
1017  $result = $this->checkPasswordValidity( $password );
1018  if ( $result->isGood() ) {
1019  return true;
1020  } else {
1021  $messages = [];
1022  foreach ( $result->getErrorsByType( 'error' ) as $error ) {
1023  $messages[] = $error['message'];
1024  }
1025  foreach ( $result->getErrorsByType( 'warning' ) as $warning ) {
1026  $messages[] = $warning['message'];
1027  }
1028  if ( count( $messages ) === 1 ) {
1029  return $messages[0];
1030  }
1031  return $messages;
1032  }
1033  }
1034 
1052  public function checkPasswordValidity( $password ) {
1054 
1055  $upp = new UserPasswordPolicy(
1056  $wgPasswordPolicy['policies'],
1057  $wgPasswordPolicy['checks']
1058  );
1059 
1061  $result = false; // init $result to false for the internal checks
1062 
1063  if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) {
1064  $status->error( $result );
1065  return $status;
1066  }
1067 
1068  if ( $result === false ) {
1069  $status->merge( $upp->checkUserPassword( $this, $password ) );
1070  return $status;
1071  } elseif ( $result === true ) {
1072  return $status;
1073  } else {
1074  $status->error( $result );
1075  return $status; // the isValidPassword hook set a string $result and returned true
1076  }
1077  }
1078 
1092  public static function getCanonicalName( $name, $validate = 'valid' ) {
1093  // Force usernames to capital
1095  $name = $wgContLang->ucfirst( $name );
1096 
1097  # Reject names containing '#'; these will be cleaned up
1098  # with title normalisation, but then it's too late to
1099  # check elsewhere
1100  if ( strpos( $name, '#' ) !== false ) {
1101  return false;
1102  }
1103 
1104  // Clean up name according to title rules,
1105  // but only when validation is requested (T14654)
1106  $t = ( $validate !== false ) ?
1108  // Check for invalid titles
1109  if ( is_null( $t ) || $t->getNamespace() !== NS_USER || $t->isExternal() ) {
1110  return false;
1111  }
1112 
1113  // Reject various classes of invalid names
1114  $name = AuthManager::callLegacyAuthPlugin(
1115  'getCanonicalName', [ $t->getText() ], $t->getText()
1116  );
1117 
1118  switch ( $validate ) {
1119  case false:
1120  break;
1121  case 'valid':
1122  if ( !self::isValidUserName( $name ) ) {
1123  $name = false;
1124  }
1125  break;
1126  case 'usable':
1127  if ( !self::isUsableName( $name ) ) {
1128  $name = false;
1129  }
1130  break;
1131  case 'creatable':
1132  if ( !self::isCreatableName( $name ) ) {
1133  $name = false;
1134  }
1135  break;
1136  default:
1137  throw new InvalidArgumentException(
1138  'Invalid parameter value for $validate in ' . __METHOD__ );
1139  }
1140  return $name;
1141  }
1142 
1149  public static function randomPassword() {
1152  }
1153 
1162  public function loadDefaults( $name = false ) {
1163  $this->mId = 0;
1164  $this->mName = $name;
1165  $this->mRealName = '';
1166  $this->mEmail = '';
1167  $this->mOptionOverrides = null;
1168  $this->mOptionsLoaded = false;
1169 
1170  $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
1171  ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1172  if ( $loggedOut !== 0 ) {
1173  $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
1174  } else {
1175  $this->mTouched = '1'; # Allow any pages to be cached
1176  }
1177 
1178  $this->mToken = null; // Don't run cryptographic functions till we need a token
1179  $this->mEmailAuthenticated = null;
1180  $this->mEmailToken = '';
1181  $this->mEmailTokenExpires = null;
1182  $this->mRegistration = wfTimestamp( TS_MW );
1183  $this->mGroupMemberships = [];
1184 
1185  Hooks::run( 'UserLoadDefaults', [ $this, $name ] );
1186  }
1187 
1200  public function isItemLoaded( $item, $all = 'all' ) {
1201  return ( $this->mLoadedItems === true && $all === 'all' ) ||
1202  ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1203  }
1204 
1210  protected function setItemLoaded( $item ) {
1211  if ( is_array( $this->mLoadedItems ) ) {
1212  $this->mLoadedItems[$item] = true;
1213  }
1214  }
1215 
1221  private function loadFromSession() {
1222  // Deprecated hook
1223  $result = null;
1224  Hooks::run( 'UserLoadFromSession', [ $this, &$result ], '1.27' );
1225  if ( $result !== null ) {
1226  return $result;
1227  }
1228 
1229  // MediaWiki\Session\Session already did the necessary authentication of the user
1230  // returned here, so just use it if applicable.
1231  $session = $this->getRequest()->getSession();
1232  $user = $session->getUser();
1233  if ( $user->isLoggedIn() ) {
1234  $this->loadFromUserObject( $user );
1235 
1236  // If this user is autoblocked, set a cookie to track the Block. This has to be done on
1237  // every session load, because an autoblocked editor might not edit again from the same
1238  // IP address after being blocked.
1239  $config = RequestContext::getMain()->getConfig();
1240  if ( $config->get( 'CookieSetOnAutoblock' ) === true ) {
1241  $block = $this->getBlock();
1242  $shouldSetCookie = $this->getRequest()->getCookie( 'BlockID' ) === null
1243  && $block
1244  && $block->getType() === Block::TYPE_USER
1245  && $block->isAutoblocking();
1246  if ( $shouldSetCookie ) {
1247  wfDebug( __METHOD__ . ': User is autoblocked, setting cookie to track' );
1248  $block->setCookie( $this->getRequest()->response() );
1249  }
1250  }
1251 
1252  // Other code expects these to be set in the session, so set them.
1253  $session->set( 'wsUserID', $this->getId() );
1254  $session->set( 'wsUserName', $this->getName() );
1255  $session->set( 'wsToken', $this->getToken() );
1256  return true;
1257  }
1258  return false;
1259  }
1260 
1268  public function loadFromDatabase( $flags = self::READ_LATEST ) {
1269  // Paranoia
1270  $this->mId = intval( $this->mId );
1271 
1272  if ( !$this->mId ) {
1273  // Anonymous users are not in the database
1274  $this->loadDefaults();
1275  return false;
1276  }
1277 
1279  $db = wfGetDB( $index );
1280 
1281  $s = $db->selectRow(
1282  'user',
1283  self::selectFields(),
1284  [ 'user_id' => $this->mId ],
1285  __METHOD__,
1286  $options
1287  );
1288 
1289  $this->queryFlagsUsed = $flags;
1290  Hooks::run( 'UserLoadFromDatabase', [ $this, &$s ] );
1291 
1292  if ( $s !== false ) {
1293  // Initialise user table data
1294  $this->loadFromRow( $s );
1295  $this->mGroupMemberships = null; // deferred
1296  $this->getEditCount(); // revalidation for nulls
1297  return true;
1298  } else {
1299  // Invalid user_id
1300  $this->mId = 0;
1301  $this->loadDefaults();
1302  return false;
1303  }
1304  }
1305 
1318  protected function loadFromRow( $row, $data = null ) {
1319  $all = true;
1320 
1321  $this->mGroupMemberships = null; // deferred
1322 
1323  if ( isset( $row->user_name ) ) {
1324  $this->mName = $row->user_name;
1325  $this->mFrom = 'name';
1326  $this->setItemLoaded( 'name' );
1327  } else {
1328  $all = false;
1329  }
1330 
1331  if ( isset( $row->user_real_name ) ) {
1332  $this->mRealName = $row->user_real_name;
1333  $this->setItemLoaded( 'realname' );
1334  } else {
1335  $all = false;
1336  }
1337 
1338  if ( isset( $row->user_id ) ) {
1339  $this->mId = intval( $row->user_id );
1340  $this->mFrom = 'id';
1341  $this->setItemLoaded( 'id' );
1342  } else {
1343  $all = false;
1344  }
1345 
1346  if ( isset( $row->user_id ) && isset( $row->user_name ) ) {
1347  self::$idCacheByName[$row->user_name] = $row->user_id;
1348  }
1349 
1350  if ( isset( $row->user_editcount ) ) {
1351  $this->mEditCount = $row->user_editcount;
1352  } else {
1353  $all = false;
1354  }
1355 
1356  if ( isset( $row->user_touched ) ) {
1357  $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
1358  } else {
1359  $all = false;
1360  }
1361 
1362  if ( isset( $row->user_token ) ) {
1363  // The definition for the column is binary(32), so trim the NULs
1364  // that appends. The previous definition was char(32), so trim
1365  // spaces too.
1366  $this->mToken = rtrim( $row->user_token, " \0" );
1367  if ( $this->mToken === '' ) {
1368  $this->mToken = null;
1369  }
1370  } else {
1371  $all = false;
1372  }
1373 
1374  if ( isset( $row->user_email ) ) {
1375  $this->mEmail = $row->user_email;
1376  $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1377  $this->mEmailToken = $row->user_email_token;
1378  $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1379  $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
1380  } else {
1381  $all = false;
1382  }
1383 
1384  if ( $all ) {
1385  $this->mLoadedItems = true;
1386  }
1387 
1388  if ( is_array( $data ) ) {
1389  if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1390  if ( !count( $data['user_groups'] ) ) {
1391  $this->mGroupMemberships = [];
1392  } else {
1393  $firstGroup = reset( $data['user_groups'] );
1394  if ( is_array( $firstGroup ) || is_object( $firstGroup ) ) {
1395  $this->mGroupMemberships = [];
1396  foreach ( $data['user_groups'] as $row ) {
1397  $ugm = UserGroupMembership::newFromRow( (object)$row );
1398  $this->mGroupMemberships[$ugm->getGroup()] = $ugm;
1399  }
1400  }
1401  }
1402  }
1403  if ( isset( $data['user_properties'] ) && is_array( $data['user_properties'] ) ) {
1404  $this->loadOptions( $data['user_properties'] );
1405  }
1406  }
1407  }
1408 
1414  protected function loadFromUserObject( $user ) {
1415  $user->load();
1416  foreach ( self::$mCacheVars as $var ) {
1417  $this->$var = $user->$var;
1418  }
1419  }
1420 
1424  private function loadGroups() {
1425  if ( is_null( $this->mGroupMemberships ) ) {
1426  $db = ( $this->queryFlagsUsed & self::READ_LATEST )
1427  ? wfGetDB( DB_MASTER )
1428  : wfGetDB( DB_REPLICA );
1429  $this->mGroupMemberships = UserGroupMembership::getMembershipsForUser(
1430  $this->mId, $db );
1431  }
1432  }
1433 
1448  public function addAutopromoteOnceGroups( $event ) {
1450 
1451  if ( wfReadOnly() || !$this->getId() ) {
1452  return [];
1453  }
1454 
1455  $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
1456  if ( !count( $toPromote ) ) {
1457  return [];
1458  }
1459 
1460  if ( !$this->checkAndSetTouched() ) {
1461  return []; // raced out (bug T48834)
1462  }
1463 
1464  $oldGroups = $this->getGroups(); // previous groups
1465  foreach ( $toPromote as $group ) {
1466  $this->addGroup( $group );
1467  }
1468  // update groups in external authentication database
1469  Hooks::run( 'UserGroupsChanged', [ $this, $toPromote, [], false, false ] );
1470  AuthManager::callLegacyAuthPlugin( 'updateExternalDBGroups', [ $this, $toPromote ] );
1471 
1472  $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
1473 
1474  $logEntry = new ManualLogEntry( 'rights', 'autopromote' );
1475  $logEntry->setPerformer( $this );
1476  $logEntry->setTarget( $this->getUserPage() );
1477  $logEntry->setParameters( [
1478  '4::oldgroups' => $oldGroups,
1479  '5::newgroups' => $newGroups,
1480  ] );
1481  $logid = $logEntry->insert();
1482  if ( $wgAutopromoteOnceLogInRC ) {
1483  $logEntry->publish( $logid );
1484  }
1485 
1486  return $toPromote;
1487  }
1488 
1498  protected function makeUpdateConditions( Database $db, array $conditions ) {
1499  if ( $this->mTouched ) {
1500  // CAS check: only update if the row wasn't changed sicne it was loaded.
1501  $conditions['user_touched'] = $db->timestamp( $this->mTouched );
1502  }
1503 
1504  return $conditions;
1505  }
1506 
1516  protected function checkAndSetTouched() {
1517  $this->load();
1518 
1519  if ( !$this->mId ) {
1520  return false; // anon
1521  }
1522 
1523  // Get a new user_touched that is higher than the old one
1524  $newTouched = $this->newTouchedTimestamp();
1525 
1526  $dbw = wfGetDB( DB_MASTER );
1527  $dbw->update( 'user',
1528  [ 'user_touched' => $dbw->timestamp( $newTouched ) ],
1529  $this->makeUpdateConditions( $dbw, [
1530  'user_id' => $this->mId,
1531  ] ),
1532  __METHOD__
1533  );
1534  $success = ( $dbw->affectedRows() > 0 );
1535 
1536  if ( $success ) {
1537  $this->mTouched = $newTouched;
1538  $this->clearSharedCache();
1539  } else {
1540  // Clears on failure too since that is desired if the cache is stale
1541  $this->clearSharedCache( 'refresh' );
1542  }
1543 
1544  return $success;
1545  }
1546 
1554  public function clearInstanceCache( $reloadFrom = false ) {
1555  $this->mNewtalk = -1;
1556  $this->mDatePreference = null;
1557  $this->mBlockedby = -1; # Unset
1558  $this->mHash = false;
1559  $this->mRights = null;
1560  $this->mEffectiveGroups = null;
1561  $this->mImplicitGroups = null;
1562  $this->mGroupMemberships = null;
1563  $this->mOptions = null;
1564  $this->mOptionsLoaded = false;
1565  $this->mEditCount = null;
1566 
1567  if ( $reloadFrom ) {
1568  $this->mLoadedItems = [];
1569  $this->mFrom = $reloadFrom;
1570  }
1571  }
1572 
1579  public static function getDefaultOptions() {
1581 
1582  static $defOpt = null;
1583  static $defOptLang = null;
1584 
1585  if ( $defOpt !== null && $defOptLang === $wgContLang->getCode() ) {
1586  // $wgContLang does not change (and should not change) mid-request,
1587  // but the unit tests change it anyway, and expect this method to
1588  // return values relevant to the current $wgContLang.
1589  return $defOpt;
1590  }
1591 
1592  $defOpt = $wgDefaultUserOptions;
1593  // Default language setting
1594  $defOptLang = $wgContLang->getCode();
1595  $defOpt['language'] = $defOptLang;
1596  foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
1597  $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
1598  }
1599 
1600  // NOTE: don't use SearchEngineConfig::getSearchableNamespaces here,
1601  // since extensions may change the set of searchable namespaces depending
1602  // on user groups/permissions.
1603  foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
1604  $defOpt['searchNs' . $nsnum] = (bool)$val;
1605  }
1606  $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
1607 
1608  Hooks::run( 'UserGetDefaultOptions', [ &$defOpt ] );
1609 
1610  return $defOpt;
1611  }
1612 
1619  public static function getDefaultOption( $opt ) {
1620  $defOpts = self::getDefaultOptions();
1621  if ( isset( $defOpts[$opt] ) ) {
1622  return $defOpts[$opt];
1623  } else {
1624  return null;
1625  }
1626  }
1627 
1634  private function getBlockedStatus( $bFromSlave = true ) {
1636 
1637  if ( -1 != $this->mBlockedby ) {
1638  return;
1639  }
1640 
1641  wfDebug( __METHOD__ . ": checking...\n" );
1642 
1643  // Initialize data...
1644  // Otherwise something ends up stomping on $this->mBlockedby when
1645  // things get lazy-loaded later, causing false positive block hits
1646  // due to -1 !== 0. Probably session-related... Nothing should be
1647  // overwriting mBlockedby, surely?
1648  $this->load();
1649 
1650  # We only need to worry about passing the IP address to the Block generator if the
1651  # user is not immune to autoblocks/hardblocks, and they are the current user so we
1652  # know which IP address they're actually coming from
1653  $ip = null;
1654  if ( !$this->isAllowed( 'ipblock-exempt' ) ) {
1655  // $wgUser->getName() only works after the end of Setup.php. Until
1656  // then, assume it's a logged-out user.
1657  $globalUserName = $wgUser->isSafeToLoad()
1658  ? $wgUser->getName()
1659  : IP::sanitizeIP( $wgUser->getRequest()->getIP() );
1660  if ( $this->getName() === $globalUserName ) {
1661  $ip = $this->getRequest()->getIP();
1662  }
1663  }
1664 
1665  // User/IP blocking
1666  $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
1667 
1668  // Cookie blocking
1669  if ( !$block instanceof Block ) {
1670  $block = $this->getBlockFromCookieValue( $this->getRequest()->getCookie( 'BlockID' ) );
1671  }
1672 
1673  // Proxy blocking
1674  if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) {
1675  // Local list
1676  if ( self::isLocallyBlockedProxy( $ip ) ) {
1677  $block = new Block( [
1678  'byText' => wfMessage( 'proxyblocker' )->text(),
1679  'reason' => wfMessage( 'proxyblockreason' )->text(),
1680  'address' => $ip,
1681  'systemBlock' => 'proxy',
1682  ] );
1683  } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
1684  $block = new Block( [
1685  'byText' => wfMessage( 'sorbs' )->text(),
1686  'reason' => wfMessage( 'sorbsreason' )->text(),
1687  'address' => $ip,
1688  'systemBlock' => 'dnsbl',
1689  ] );
1690  }
1691  }
1692 
1693  // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
1694  if ( !$block instanceof Block
1696  && $ip !== null
1697  && !in_array( $ip, $wgProxyWhitelist )
1698  ) {
1699  $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
1700  $xff = array_map( 'trim', explode( ',', $xff ) );
1701  $xff = array_diff( $xff, [ $ip ] );
1702  $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromSlave );
1703  $block = Block::chooseBlock( $xffblocks, $xff );
1704  if ( $block instanceof Block ) {
1705  # Mangle the reason to alert the user that the block
1706  # originated from matching the X-Forwarded-For header.
1707  $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->text();
1708  }
1709  }
1710 
1711  if ( !$block instanceof Block
1712  && $ip !== null
1713  && $this->isAnon()
1715  ) {
1716  $block = new Block( [
1717  'address' => $ip,
1718  'byText' => 'MediaWiki default',
1719  'reason' => wfMessage( 'softblockrangesreason', $ip )->text(),
1720  'anonOnly' => true,
1721  'systemBlock' => 'wgSoftBlockRanges',
1722  ] );
1723  }
1724 
1725  if ( $block instanceof Block ) {
1726  wfDebug( __METHOD__ . ": Found block.\n" );
1727  $this->mBlock = $block;
1728  $this->mBlockedby = $block->getByName();
1729  $this->mBlockreason = $block->mReason;
1730  $this->mHideName = $block->mHideName;
1731  $this->mAllowUsertalk = !$block->prevents( 'editownusertalk' );
1732  } else {
1733  $this->mBlockedby = '';
1734  $this->mHideName = 0;
1735  $this->mAllowUsertalk = false;
1736  }
1737 
1738  // Avoid PHP 7.1 warning of passing $this by reference
1739  $user = $this;
1740  // Extensions
1741  Hooks::run( 'GetBlockedStatus', [ &$user ] );
1742  }
1743 
1749  protected function getBlockFromCookieValue( $blockCookieVal ) {
1750  // Make sure there's something to check. The cookie value must start with a number.
1751  if ( strlen( $blockCookieVal ) < 1 || !is_numeric( substr( $blockCookieVal, 0, 1 ) ) ) {
1752  return false;
1753  }
1754  // Load the Block from the ID in the cookie.
1755  $blockCookieId = Block::getIdFromCookieValue( $blockCookieVal );
1756  if ( $blockCookieId !== null ) {
1757  // An ID was found in the cookie.
1758  $tmpBlock = Block::newFromID( $blockCookieId );
1759  if ( $tmpBlock instanceof Block ) {
1760  // Check the validity of the block.
1761  $blockIsValid = $tmpBlock->getType() == Block::TYPE_USER
1762  && !$tmpBlock->isExpired()
1763  && $tmpBlock->isAutoblocking();
1764  $config = RequestContext::getMain()->getConfig();
1765  $useBlockCookie = ( $config->get( 'CookieSetOnAutoblock' ) === true );
1766  if ( $blockIsValid && $useBlockCookie ) {
1767  // Use the block.
1768  return $tmpBlock;
1769  } else {
1770  // If the block is not valid, remove the cookie.
1771  Block::clearCookie( $this->getRequest()->response() );
1772  }
1773  } else {
1774  // If the block doesn't exist, remove the cookie.
1775  Block::clearCookie( $this->getRequest()->response() );
1776  }
1777  }
1778  return false;
1779  }
1780 
1788  public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
1790 
1791  if ( !$wgEnableDnsBlacklist ) {
1792  return false;
1793  }
1794 
1795  if ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) ) {
1796  return false;
1797  }
1798 
1799  return $this->inDnsBlacklist( $ip, $wgDnsBlacklistUrls );
1800  }
1801 
1809  public function inDnsBlacklist( $ip, $bases ) {
1810  $found = false;
1811  // @todo FIXME: IPv6 ??? (https://bugs.php.net/bug.php?id=33170)
1812  if ( IP::isIPv4( $ip ) ) {
1813  // Reverse IP, T23255
1814  $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
1815 
1816  foreach ( (array)$bases as $base ) {
1817  // Make hostname
1818  // If we have an access key, use that too (ProjectHoneypot, etc.)
1819  $basename = $base;
1820  if ( is_array( $base ) ) {
1821  if ( count( $base ) >= 2 ) {
1822  // Access key is 1, base URL is 0
1823  $host = "{$base[1]}.$ipReversed.{$base[0]}";
1824  } else {
1825  $host = "$ipReversed.{$base[0]}";
1826  }
1827  $basename = $base[0];
1828  } else {
1829  $host = "$ipReversed.$base";
1830  }
1831 
1832  // Send query
1833  $ipList = gethostbynamel( $host );
1834 
1835  if ( $ipList ) {
1836  wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
1837  $found = true;
1838  break;
1839  } else {
1840  wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." );
1841  }
1842  }
1843  }
1844 
1845  return $found;
1846  }
1847 
1855  public static function isLocallyBlockedProxy( $ip ) {
1857 
1858  if ( !$wgProxyList ) {
1859  return false;
1860  }
1861 
1862  if ( !is_array( $wgProxyList ) ) {
1863  // Load values from the specified file
1864  $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
1865  }
1866 
1867  $resultProxyList = [];
1868  $deprecatedIPEntries = [];
1869 
1870  // backward compatibility: move all ip addresses in keys to values
1871  foreach ( $wgProxyList as $key => $value ) {
1872  $keyIsIP = IP::isIPAddress( $key );
1873  $valueIsIP = IP::isIPAddress( $value );
1874  if ( $keyIsIP && !$valueIsIP ) {
1875  $deprecatedIPEntries[] = $key;
1876  $resultProxyList[] = $key;
1877  } elseif ( $keyIsIP && $valueIsIP ) {
1878  $deprecatedIPEntries[] = $key;
1879  $resultProxyList[] = $key;
1880  $resultProxyList[] = $value;
1881  } else {
1882  $resultProxyList[] = $value;
1883  }
1884  }
1885 
1886  if ( $deprecatedIPEntries ) {
1887  wfDeprecated(
1888  'IP addresses in the keys of $wgProxyList (found the following IP addresses in keys: ' .
1889  implode( ', ', $deprecatedIPEntries ) . ', please move them to values)', '1.30' );
1890  }
1891 
1892  $proxyListIPSet = new IPSet( $resultProxyList );
1893  return $proxyListIPSet->match( $ip );
1894  }
1895 
1901  public function isPingLimitable() {
1903  if ( IP::isInRanges( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
1904  // No other good way currently to disable rate limits
1905  // for specific IPs. :P
1906  // But this is a crappy hack and should die.
1907  return false;
1908  }
1909  return !$this->isAllowed( 'noratelimit' );
1910  }
1911 
1926  public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1927  // Avoid PHP 7.1 warning of passing $this by reference
1928  $user = $this;
1929  // Call the 'PingLimiter' hook
1930  $result = false;
1931  if ( !Hooks::run( 'PingLimiter', [ &$user, $action, &$result, $incrBy ] ) ) {
1932  return $result;
1933  }
1934 
1936  if ( !isset( $wgRateLimits[$action] ) ) {
1937  return false;
1938  }
1939 
1940  $limits = array_merge(
1941  [ '&can-bypass' => true ],
1942  $wgRateLimits[$action]
1943  );
1944 
1945  // Some groups shouldn't trigger the ping limiter, ever
1946  if ( $limits['&can-bypass'] && !$this->isPingLimitable() ) {
1947  return false;
1948  }
1949 
1950  $keys = [];
1951  $id = $this->getId();
1952  $userLimit = false;
1953  $isNewbie = $this->isNewbie();
1955 
1956  if ( $id == 0 ) {
1957  // limits for anons
1958  if ( isset( $limits['anon'] ) ) {
1959  $keys[$cache->makeKey( 'limiter', $action, 'anon' )] = $limits['anon'];
1960  }
1961  } else {
1962  // limits for logged-in users
1963  if ( isset( $limits['user'] ) ) {
1964  $userLimit = $limits['user'];
1965  }
1966  // limits for newbie logged-in users
1967  if ( $isNewbie && isset( $limits['newbie'] ) ) {
1968  $keys[$cache->makeKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
1969  }
1970  }
1971 
1972  // limits for anons and for newbie logged-in users
1973  if ( $isNewbie ) {
1974  // ip-based limits
1975  if ( isset( $limits['ip'] ) ) {
1976  $ip = $this->getRequest()->getIP();
1977  $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
1978  }
1979  // subnet-based limits
1980  if ( isset( $limits['subnet'] ) ) {
1981  $ip = $this->getRequest()->getIP();
1982  $subnet = IP::getSubnet( $ip );
1983  if ( $subnet !== false ) {
1984  $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
1985  }
1986  }
1987  }
1988 
1989  // Check for group-specific permissions
1990  // If more than one group applies, use the group with the highest limit ratio (max/period)
1991  foreach ( $this->getGroups() as $group ) {
1992  if ( isset( $limits[$group] ) ) {
1993  if ( $userLimit === false
1994  || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
1995  ) {
1996  $userLimit = $limits[$group];
1997  }
1998  }
1999  }
2000 
2001  // Set the user limit key
2002  if ( $userLimit !== false ) {
2003  list( $max, $period ) = $userLimit;
2004  wfDebug( __METHOD__ . ": effective user limit: $max in {$period}s\n" );
2005  $keys[$cache->makeKey( 'limiter', $action, 'user', $id )] = $userLimit;
2006  }
2007 
2008  // ip-based limits for all ping-limitable users
2009  if ( isset( $limits['ip-all'] ) ) {
2010  $ip = $this->getRequest()->getIP();
2011  // ignore if user limit is more permissive
2012  if ( $isNewbie || $userLimit === false
2013  || $limits['ip-all'][0] / $limits['ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
2014  $keys["mediawiki:limiter:$action:ip-all:$ip"] = $limits['ip-all'];
2015  }
2016  }
2017 
2018  // subnet-based limits for all ping-limitable users
2019  if ( isset( $limits['subnet-all'] ) ) {
2020  $ip = $this->getRequest()->getIP();
2021  $subnet = IP::getSubnet( $ip );
2022  if ( $subnet !== false ) {
2023  // ignore if user limit is more permissive
2024  if ( $isNewbie || $userLimit === false
2025  || $limits['ip-all'][0] / $limits['ip-all'][1]
2026  > $userLimit[0] / $userLimit[1] ) {
2027  $keys["mediawiki:limiter:$action:subnet-all:$subnet"] = $limits['subnet-all'];
2028  }
2029  }
2030  }
2031 
2032  $triggered = false;
2033  foreach ( $keys as $key => $limit ) {
2034  list( $max, $period ) = $limit;
2035  $summary = "(limit $max in {$period}s)";
2036  $count = $cache->get( $key );
2037  // Already pinged?
2038  if ( $count ) {
2039  if ( $count >= $max ) {
2040  wfDebugLog( 'ratelimit', "User '{$this->getName()}' " .
2041  "(IP {$this->getRequest()->getIP()}) tripped $key at $count $summary" );
2042  $triggered = true;
2043  } else {
2044  wfDebug( __METHOD__ . ": ok. $key at $count $summary\n" );
2045  }
2046  } else {
2047  wfDebug( __METHOD__ . ": adding record for $key $summary\n" );
2048  if ( $incrBy > 0 ) {
2049  $cache->add( $key, 0, intval( $period ) ); // first ping
2050  }
2051  }
2052  if ( $incrBy > 0 ) {
2053  $cache->incr( $key, $incrBy );
2054  }
2055  }
2056 
2057  return $triggered;
2058  }
2059 
2067  public function isBlocked( $bFromSlave = true ) {
2068  return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
2069  }
2070 
2077  public function getBlock( $bFromSlave = true ) {
2078  $this->getBlockedStatus( $bFromSlave );
2079  return $this->mBlock instanceof Block ? $this->mBlock : null;
2080  }
2081 
2089  public function isBlockedFrom( $title, $bFromSlave = false ) {
2091 
2092  $blocked = $this->isBlocked( $bFromSlave );
2093  $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
2094  // If a user's name is suppressed, they cannot make edits anywhere
2095  if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName()
2096  && $title->getNamespace() == NS_USER_TALK ) {
2097  $blocked = false;
2098  wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
2099  }
2100 
2101  Hooks::run( 'UserIsBlockedFrom', [ $this, $title, &$blocked, &$allowUsertalk ] );
2102 
2103  return $blocked;
2104  }
2105 
2110  public function blockedBy() {
2111  $this->getBlockedStatus();
2112  return $this->mBlockedby;
2113  }
2114 
2119  public function blockedFor() {
2120  $this->getBlockedStatus();
2121  return $this->mBlockreason;
2122  }
2123 
2128  public function getBlockId() {
2129  $this->getBlockedStatus();
2130  return ( $this->mBlock ? $this->mBlock->getId() : false );
2131  }
2132 
2141  public function isBlockedGlobally( $ip = '' ) {
2142  return $this->getGlobalBlock( $ip ) instanceof Block;
2143  }
2144 
2155  public function getGlobalBlock( $ip = '' ) {
2156  if ( $this->mGlobalBlock !== null ) {
2157  return $this->mGlobalBlock ?: null;
2158  }
2159  // User is already an IP?
2160  if ( IP::isIPAddress( $this->getName() ) ) {
2161  $ip = $this->getName();
2162  } elseif ( !$ip ) {
2163  $ip = $this->getRequest()->getIP();
2164  }
2165  // Avoid PHP 7.1 warning of passing $this by reference
2166  $user = $this;
2167  $blocked = false;
2168  $block = null;
2169  Hooks::run( 'UserIsBlockedGlobally', [ &$user, $ip, &$blocked, &$block ] );
2170 
2171  if ( $blocked && $block === null ) {
2172  // back-compat: UserIsBlockedGlobally didn't have $block param first
2173  $block = new Block( [
2174  'address' => $ip,
2175  'systemBlock' => 'global-block'
2176  ] );
2177  }
2178 
2179  $this->mGlobalBlock = $blocked ? $block : false;
2180  return $this->mGlobalBlock ?: null;
2181  }
2182 
2188  public function isLocked() {
2189  if ( $this->mLocked !== null ) {
2190  return $this->mLocked;
2191  }
2192  // Avoid PHP 7.1 warning of passing $this by reference
2193  $user = $this;
2194  $authUser = AuthManager::callLegacyAuthPlugin( 'getUserInstance', [ &$user ], null );
2195  $this->mLocked = $authUser && $authUser->isLocked();
2196  Hooks::run( 'UserIsLocked', [ $this, &$this->mLocked ] );
2197  return $this->mLocked;
2198  }
2199 
2205  public function isHidden() {
2206  if ( $this->mHideName !== null ) {
2207  return $this->mHideName;
2208  }
2209  $this->getBlockedStatus();
2210  if ( !$this->mHideName ) {
2211  // Avoid PHP 7.1 warning of passing $this by reference
2212  $user = $this;
2213  $authUser = AuthManager::callLegacyAuthPlugin( 'getUserInstance', [ &$user ], null );
2214  $this->mHideName = $authUser && $authUser->isHidden();
2215  Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
2216  }
2217  return $this->mHideName;
2218  }
2219 
2224  public function getId() {
2225  if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
2226  // Special case, we know the user is anonymous
2227  return 0;
2228  } elseif ( !$this->isItemLoaded( 'id' ) ) {
2229  // Don't load if this was initialized from an ID
2230  $this->load();
2231  }
2232 
2233  return (int)$this->mId;
2234  }
2235 
2240  public function setId( $v ) {
2241  $this->mId = $v;
2242  $this->clearInstanceCache( 'id' );
2243  }
2244 
2249  public function getName() {
2250  if ( $this->isItemLoaded( 'name', 'only' ) ) {
2251  // Special case optimisation
2252  return $this->mName;
2253  } else {
2254  $this->load();
2255  if ( $this->mName === false ) {
2256  // Clean up IPs
2257  $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
2258  }
2259  return $this->mName;
2260  }
2261  }
2262 
2276  public function setName( $str ) {
2277  $this->load();
2278  $this->mName = $str;
2279  }
2280 
2285  public function getTitleKey() {
2286  return str_replace( ' ', '_', $this->getName() );
2287  }
2288 
2293  public function getNewtalk() {
2294  $this->load();
2295 
2296  // Load the newtalk status if it is unloaded (mNewtalk=-1)
2297  if ( $this->mNewtalk === -1 ) {
2298  $this->mNewtalk = false; # reset talk page status
2299 
2300  // Check memcached separately for anons, who have no
2301  // entire User object stored in there.
2302  if ( !$this->mId ) {
2304  if ( $wgDisableAnonTalk ) {
2305  // Anon newtalk disabled by configuration.
2306  $this->mNewtalk = false;
2307  } else {
2308  $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName() );
2309  }
2310  } else {
2311  $this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
2312  }
2313  }
2314 
2315  return (bool)$this->mNewtalk;
2316  }
2317 
2331  public function getNewMessageLinks() {
2332  // Avoid PHP 7.1 warning of passing $this by reference
2333  $user = $this;
2334  $talks = [];
2335  if ( !Hooks::run( 'UserRetrieveNewTalks', [ &$user, &$talks ] ) ) {
2336  return $talks;
2337  } elseif ( !$this->getNewtalk() ) {
2338  return [];
2339  }
2340  $utp = $this->getTalkPage();
2341  $dbr = wfGetDB( DB_REPLICA );
2342  // Get the "last viewed rev" timestamp from the oldest message notification
2343  $timestamp = $dbr->selectField( 'user_newtalk',
2344  'MIN(user_last_timestamp)',
2345  $this->isAnon() ? [ 'user_ip' => $this->getName() ] : [ 'user_id' => $this->getId() ],
2346  __METHOD__ );
2347  $rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
2348  return [ [ 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ] ];
2349  }
2350 
2356  public function getNewMessageRevisionId() {
2357  $newMessageRevisionId = null;
2358  $newMessageLinks = $this->getNewMessageLinks();
2359  if ( $newMessageLinks ) {
2360  // Note: getNewMessageLinks() never returns more than a single link
2361  // and it is always for the same wiki, but we double-check here in
2362  // case that changes some time in the future.
2363  if ( count( $newMessageLinks ) === 1
2364  && $newMessageLinks[0]['wiki'] === wfWikiID()
2365  && $newMessageLinks[0]['rev']
2366  ) {
2368  $newMessageRevision = $newMessageLinks[0]['rev'];
2369  $newMessageRevisionId = $newMessageRevision->getId();
2370  }
2371  }
2372  return $newMessageRevisionId;
2373  }
2374 
2383  protected function checkNewtalk( $field, $id ) {
2384  $dbr = wfGetDB( DB_REPLICA );
2385 
2386  $ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
2387 
2388  return $ok !== false;
2389  }
2390 
2398  protected function updateNewtalk( $field, $id, $curRev = null ) {
2399  // Get timestamp of the talk page revision prior to the current one
2400  $prevRev = $curRev ? $curRev->getPrevious() : false;
2401  $ts = $prevRev ? $prevRev->getTimestamp() : null;
2402  // Mark the user as having new messages since this revision
2403  $dbw = wfGetDB( DB_MASTER );
2404  $dbw->insert( 'user_newtalk',
2405  [ $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ],
2406  __METHOD__,
2407  'IGNORE' );
2408  if ( $dbw->affectedRows() ) {
2409  wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
2410  return true;
2411  } else {
2412  wfDebug( __METHOD__ . " already set ($field, $id)\n" );
2413  return false;
2414  }
2415  }
2416 
2423  protected function deleteNewtalk( $field, $id ) {
2424  $dbw = wfGetDB( DB_MASTER );
2425  $dbw->delete( 'user_newtalk',
2426  [ $field => $id ],
2427  __METHOD__ );
2428  if ( $dbw->affectedRows() ) {
2429  wfDebug( __METHOD__ . ": killed on ($field, $id)\n" );
2430  return true;
2431  } else {
2432  wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
2433  return false;
2434  }
2435  }
2436 
2443  public function setNewtalk( $val, $curRev = null ) {
2444  if ( wfReadOnly() ) {
2445  return;
2446  }
2447 
2448  $this->load();
2449  $this->mNewtalk = $val;
2450 
2451  if ( $this->isAnon() ) {
2452  $field = 'user_ip';
2453  $id = $this->getName();
2454  } else {
2455  $field = 'user_id';
2456  $id = $this->getId();
2457  }
2458 
2459  if ( $val ) {
2460  $changed = $this->updateNewtalk( $field, $id, $curRev );
2461  } else {
2462  $changed = $this->deleteNewtalk( $field, $id );
2463  }
2464 
2465  if ( $changed ) {
2466  $this->invalidateCache();
2467  }
2468  }
2469 
2475  private function newTouchedTimestamp() {
2477 
2478  $time = wfTimestamp( TS_MW, time() + $wgClockSkewFudge );
2479  if ( $this->mTouched && $time <= $this->mTouched ) {
2480  $time = wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $this->mTouched ) + 1 );
2481  }
2482 
2483  return $time;
2484  }
2485 
2496  public function clearSharedCache( $mode = 'changed' ) {
2497  if ( !$this->getId() ) {
2498  return;
2499  }
2500 
2502  $key = $this->getCacheKey( $cache );
2503  if ( $mode === 'refresh' ) {
2504  $cache->delete( $key, 1 );
2505  } else {
2506  wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
2507  function () use ( $cache, $key ) {
2508  $cache->delete( $key );
2509  },
2510  __METHOD__
2511  );
2512  }
2513  }
2514 
2520  public function invalidateCache() {
2521  $this->touch();
2522  $this->clearSharedCache();
2523  }
2524 
2537  public function touch() {
2538  $id = $this->getId();
2539  if ( $id ) {
2540  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2541  $key = $cache->makeKey( 'user-quicktouched', 'id', $id );
2542  $cache->touchCheckKey( $key );
2543  $this->mQuickTouched = null;
2544  }
2545  }
2546 
2552  public function validateCache( $timestamp ) {
2553  return ( $timestamp >= $this->getTouched() );
2554  }
2555 
2564  public function getTouched() {
2565  $this->load();
2566 
2567  if ( $this->mId ) {
2568  if ( $this->mQuickTouched === null ) {
2569  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2570  $key = $cache->makeKey( 'user-quicktouched', 'id', $this->mId );
2571 
2572  $this->mQuickTouched = wfTimestamp( TS_MW, $cache->getCheckKeyTime( $key ) );
2573  }
2574 
2575  return max( $this->mTouched, $this->mQuickTouched );
2576  }
2577 
2578  return $this->mTouched;
2579  }
2580 
2586  public function getDBTouched() {
2587  $this->load();
2588 
2589  return $this->mTouched;
2590  }
2591 
2608  public function setPassword( $str ) {
2609  return $this->setPasswordInternal( $str );
2610  }
2611 
2620  public function setInternalPassword( $str ) {
2621  $this->setPasswordInternal( $str );
2622  }
2623 
2632  private function setPasswordInternal( $str ) {
2633  $manager = AuthManager::singleton();
2634 
2635  // If the user doesn't exist yet, fail
2636  if ( !$manager->userExists( $this->getName() ) ) {
2637  throw new LogicException( 'Cannot set a password for a user that is not in the database.' );
2638  }
2639 
2640  $status = $this->changeAuthenticationData( [
2641  'username' => $this->getName(),
2642  'password' => $str,
2643  'retype' => $str,
2644  ] );
2645  if ( !$status->isGood() ) {
2647  ->info( __METHOD__ . ': Password change rejected: '
2648  . $status->getWikiText( null, null, 'en' ) );
2649  return false;
2650  }
2651 
2652  $this->setOption( 'watchlisttoken', false );
2653  SessionManager::singleton()->invalidateSessionsForUser( $this );
2654 
2655  return true;
2656  }
2657 
2670  public function changeAuthenticationData( array $data ) {
2671  $manager = AuthManager::singleton();
2672  $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2673  $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2674 
2675  $status = Status::newGood( 'ignored' );
2676  foreach ( $reqs as $req ) {
2677  $status->merge( $manager->allowsAuthenticationDataChange( $req ), true );
2678  }
2679  if ( $status->getValue() === 'ignored' ) {
2680  $status->warning( 'authenticationdatachange-ignored' );
2681  }
2682 
2683  if ( $status->isGood() ) {
2684  foreach ( $reqs as $req ) {
2685  $manager->changeAuthenticationData( $req );
2686  }
2687  }
2688  return $status;
2689  }
2690 
2697  public function getToken( $forceCreation = true ) {
2699 
2700  $this->load();
2701  if ( !$this->mToken && $forceCreation ) {
2702  $this->setToken();
2703  }
2704 
2705  if ( !$this->mToken ) {
2706  // The user doesn't have a token, return null to indicate that.
2707  return null;
2708  } elseif ( $this->mToken === self::INVALID_TOKEN ) {
2709  // We return a random value here so existing token checks are very
2710  // likely to fail.
2711  return MWCryptRand::generateHex( self::TOKEN_LENGTH );
2712  } elseif ( $wgAuthenticationTokenVersion === null ) {
2713  // $wgAuthenticationTokenVersion not in use, so return the raw secret
2714  return $this->mToken;
2715  } else {
2716  // $wgAuthenticationTokenVersion in use, so hmac it.
2717  $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
2718 
2719  // The raw hash can be overly long. Shorten it up.
2720  $len = max( 32, self::TOKEN_LENGTH );
2721  if ( strlen( $ret ) < $len ) {
2722  // Should never happen, even md5 is 128 bits
2723  throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
2724  }
2725  return substr( $ret, -$len );
2726  }
2727  }
2728 
2735  public function setToken( $token = false ) {
2736  $this->load();
2737  if ( $this->mToken === self::INVALID_TOKEN ) {
2739  ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
2740  } elseif ( !$token ) {
2741  $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
2742  } else {
2743  $this->mToken = $token;
2744  }
2745  }
2746 
2755  public function setNewpassword( $str, $throttle = true ) {
2756  throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
2757  }
2758 
2763  public function getEmail() {
2764  $this->load();
2765  Hooks::run( 'UserGetEmail', [ $this, &$this->mEmail ] );
2766  return $this->mEmail;
2767  }
2768 
2774  $this->load();
2775  Hooks::run( 'UserGetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
2777  }
2778 
2783  public function setEmail( $str ) {
2784  $this->load();
2785  if ( $str == $this->mEmail ) {
2786  return;
2787  }
2788  $this->invalidateEmail();
2789  $this->mEmail = $str;
2790  Hooks::run( 'UserSetEmail', [ $this, &$this->mEmail ] );
2791  }
2792 
2800  public function setEmailWithConfirmation( $str ) {
2802 
2803  if ( !$wgEnableEmail ) {
2804  return Status::newFatal( 'emaildisabled' );
2805  }
2806 
2807  $oldaddr = $this->getEmail();
2808  if ( $str === $oldaddr ) {
2809  return Status::newGood( true );
2810  }
2811 
2812  $type = $oldaddr != '' ? 'changed' : 'set';
2813  $notificationResult = null;
2814 
2815  if ( $wgEmailAuthentication ) {
2816  // Send the user an email notifying the user of the change in registered
2817  // email address on their previous email address
2818  if ( $type == 'changed' ) {
2819  $change = $str != '' ? 'changed' : 'removed';
2820  $notificationResult = $this->sendMail(
2821  wfMessage( 'notificationemail_subject_' . $change )->text(),
2822  wfMessage( 'notificationemail_body_' . $change,
2823  $this->getRequest()->getIP(),
2824  $this->getName(),
2825  $str )->text()
2826  );
2827  }
2828  }
2829 
2830  $this->setEmail( $str );
2831 
2832  if ( $str !== '' && $wgEmailAuthentication ) {
2833  // Send a confirmation request to the new address if needed
2834  $result = $this->sendConfirmationMail( $type );
2835 
2836  if ( $notificationResult !== null ) {
2837  $result->merge( $notificationResult );
2838  }
2839 
2840  if ( $result->isGood() ) {
2841  // Say to the caller that a confirmation and notification mail has been sent
2842  $result->value = 'eauth';
2843  }
2844  } else {
2845  $result = Status::newGood( true );
2846  }
2847 
2848  return $result;
2849  }
2850 
2855  public function getRealName() {
2856  if ( !$this->isItemLoaded( 'realname' ) ) {
2857  $this->load();
2858  }
2859 
2860  return $this->mRealName;
2861  }
2862 
2867  public function setRealName( $str ) {
2868  $this->load();
2869  $this->mRealName = $str;
2870  }
2871 
2882  public function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
2884  $this->loadOptions();
2885 
2886  # We want 'disabled' preferences to always behave as the default value for
2887  # users, even if they have set the option explicitly in their settings (ie they
2888  # set it, and then it was disabled removing their ability to change it). But
2889  # we don't want to erase the preferences in the database in case the preference
2890  # is re-enabled again. So don't touch $mOptions, just override the returned value
2891  if ( !$ignoreHidden && in_array( $oname, $wgHiddenPrefs ) ) {
2892  return self::getDefaultOption( $oname );
2893  }
2894 
2895  if ( array_key_exists( $oname, $this->mOptions ) ) {
2896  return $this->mOptions[$oname];
2897  } else {
2898  return $defaultOverride;
2899  }
2900  }
2901 
2910  public function getOptions( $flags = 0 ) {
2912  $this->loadOptions();
2914 
2915  # We want 'disabled' preferences to always behave as the default value for
2916  # users, even if they have set the option explicitly in their settings (ie they
2917  # set it, and then it was disabled removing their ability to change it). But
2918  # we don't want to erase the preferences in the database in case the preference
2919  # is re-enabled again. So don't touch $mOptions, just override the returned value
2920  foreach ( $wgHiddenPrefs as $pref ) {
2921  $default = self::getDefaultOption( $pref );
2922  if ( $default !== null ) {
2923  $options[$pref] = $default;
2924  }
2925  }
2926 
2927  if ( $flags & self::GETOPTIONS_EXCLUDE_DEFAULTS ) {
2928  $options = array_diff_assoc( $options, self::getDefaultOptions() );
2929  }
2930 
2931  return $options;
2932  }
2933 
2941  public function getBoolOption( $oname ) {
2942  return (bool)$this->getOption( $oname );
2943  }
2944 
2953  public function getIntOption( $oname, $defaultOverride = 0 ) {
2954  $val = $this->getOption( $oname );
2955  if ( $val == '' ) {
2956  $val = $defaultOverride;
2957  }
2958  return intval( $val );
2959  }
2960 
2969  public function setOption( $oname, $val ) {
2970  $this->loadOptions();
2971 
2972  // Explicitly NULL values should refer to defaults
2973  if ( is_null( $val ) ) {
2974  $val = self::getDefaultOption( $oname );
2975  }
2976 
2977  $this->mOptions[$oname] = $val;
2978  }
2979 
2990  public function getTokenFromOption( $oname ) {
2992 
2993  $id = $this->getId();
2994  if ( !$id || in_array( $oname, $wgHiddenPrefs ) ) {
2995  return false;
2996  }
2997 
2998  $token = $this->getOption( $oname );
2999  if ( !$token ) {
3000  // Default to a value based on the user token to avoid space
3001  // wasted on storing tokens for all users. When this option
3002  // is set manually by the user, only then is it stored.
3003  $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
3004  }
3005 
3006  return $token;
3007  }
3008 
3018  public function resetTokenFromOption( $oname ) {
3020  if ( in_array( $oname, $wgHiddenPrefs ) ) {
3021  return false;
3022  }
3023 
3024  $token = MWCryptRand::generateHex( 40 );
3025  $this->setOption( $oname, $token );
3026  return $token;
3027  }
3028 
3052  public static function listOptionKinds() {
3053  return [
3054  'registered',
3055  'registered-multiselect',
3056  'registered-checkmatrix',
3057  'userjs',
3058  'special',
3059  'unused'
3060  ];
3061  }
3062 
3075  public function getOptionKinds( IContextSource $context, $options = null ) {
3076  $this->loadOptions();
3077  if ( $options === null ) {
3079  }
3080 
3081  $prefs = Preferences::getPreferences( $this, $context );
3082  $mapping = [];
3083 
3084  // Pull out the "special" options, so they don't get converted as
3085  // multiselect or checkmatrix.
3086  $specialOptions = array_fill_keys( Preferences::getSaveBlacklist(), true );
3087  foreach ( $specialOptions as $name => $value ) {
3088  unset( $prefs[$name] );
3089  }
3090 
3091  // Multiselect and checkmatrix options are stored in the database with
3092  // one key per option, each having a boolean value. Extract those keys.
3093  $multiselectOptions = [];
3094  foreach ( $prefs as $name => $info ) {
3095  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
3096  ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
3097  $opts = HTMLFormField::flattenOptions( $info['options'] );
3098  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
3099 
3100  foreach ( $opts as $value ) {
3101  $multiselectOptions["$prefix$value"] = true;
3102  }
3103 
3104  unset( $prefs[$name] );
3105  }
3106  }
3107  $checkmatrixOptions = [];
3108  foreach ( $prefs as $name => $info ) {
3109  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
3110  ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
3111  $columns = HTMLFormField::flattenOptions( $info['columns'] );
3112  $rows = HTMLFormField::flattenOptions( $info['rows'] );
3113  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
3114 
3115  foreach ( $columns as $column ) {
3116  foreach ( $rows as $row ) {
3117  $checkmatrixOptions["$prefix$column-$row"] = true;
3118  }
3119  }
3120 
3121  unset( $prefs[$name] );
3122  }
3123  }
3124 
3125  // $value is ignored
3126  foreach ( $options as $key => $value ) {
3127  if ( isset( $prefs[$key] ) ) {
3128  $mapping[$key] = 'registered';
3129  } elseif ( isset( $multiselectOptions[$key] ) ) {
3130  $mapping[$key] = 'registered-multiselect';
3131  } elseif ( isset( $checkmatrixOptions[$key] ) ) {
3132  $mapping[$key] = 'registered-checkmatrix';
3133  } elseif ( isset( $specialOptions[$key] ) ) {
3134  $mapping[$key] = 'special';
3135  } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
3136  $mapping[$key] = 'userjs';
3137  } else {
3138  $mapping[$key] = 'unused';
3139  }
3140  }
3141 
3142  return $mapping;
3143  }
3144 
3159  public function resetOptions(
3160  $resetKinds = [ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ],
3161  IContextSource $context = null
3162  ) {
3163  $this->load();
3164  $defaultOptions = self::getDefaultOptions();
3165 
3166  if ( !is_array( $resetKinds ) ) {
3167  $resetKinds = [ $resetKinds ];
3168  }
3169 
3170  if ( in_array( 'all', $resetKinds ) ) {
3171  $newOptions = $defaultOptions;
3172  } else {
3173  if ( $context === null ) {
3175  }
3176 
3177  $optionKinds = $this->getOptionKinds( $context );
3178  $resetKinds = array_intersect( $resetKinds, self::listOptionKinds() );
3179  $newOptions = [];
3180 
3181  // Use default values for the options that should be deleted, and
3182  // copy old values for the ones that shouldn't.
3183  foreach ( $this->mOptions as $key => $value ) {
3184  if ( in_array( $optionKinds[$key], $resetKinds ) ) {
3185  if ( array_key_exists( $key, $defaultOptions ) ) {
3186  $newOptions[$key] = $defaultOptions[$key];
3187  }
3188  } else {
3189  $newOptions[$key] = $value;
3190  }
3191  }
3192  }
3193 
3194  Hooks::run( 'UserResetAllOptions', [ $this, &$newOptions, $this->mOptions, $resetKinds ] );
3195 
3196  $this->mOptions = $newOptions;
3197  $this->mOptionsLoaded = true;
3198  }
3199 
3204  public function getDatePreference() {
3205  // Important migration for old data rows
3206  if ( is_null( $this->mDatePreference ) ) {
3207  global $wgLang;
3208  $value = $this->getOption( 'date' );
3209  $map = $wgLang->getDatePreferenceMigrationMap();
3210  if ( isset( $map[$value] ) ) {
3211  $value = $map[$value];
3212  }
3213  $this->mDatePreference = $value;
3214  }
3215  return $this->mDatePreference;
3216  }
3217 
3224  public function requiresHTTPS() {
3226  if ( !$wgSecureLogin ) {
3227  return false;
3228  } else {
3229  $https = $this->getBoolOption( 'prefershttps' );
3230  Hooks::run( 'UserRequiresHTTPS', [ $this, &$https ] );
3231  if ( $https ) {
3232  $https = wfCanIPUseHTTPS( $this->getRequest()->getIP() );
3233  }
3234  return $https;
3235  }
3236  }
3237 
3243  public function getStubThreshold() {
3244  global $wgMaxArticleSize; # Maximum article size, in Kb
3245  $threshold = $this->getIntOption( 'stubthreshold' );
3246  if ( $threshold > $wgMaxArticleSize * 1024 ) {
3247  // If they have set an impossible value, disable the preference
3248  // so we can use the parser cache again.
3249  $threshold = 0;
3250  }
3251  return $threshold;
3252  }
3253 
3258  public function getRights() {
3259  if ( is_null( $this->mRights ) ) {
3260  $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
3261  Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
3262 
3263  // Deny any rights denied by the user's session, unless this
3264  // endpoint has no sessions.
3265  if ( !defined( 'MW_NO_SESSION' ) ) {
3266  $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
3267  if ( $allowedRights !== null ) {
3268  $this->mRights = array_intersect( $this->mRights, $allowedRights );
3269  }
3270  }
3271 
3272  // Force reindexation of rights when a hook has unset one of them
3273  $this->mRights = array_values( array_unique( $this->mRights ) );
3274 
3275  // If block disables login, we should also remove any
3276  // extra rights blocked users might have, in case the
3277  // blocked user has a pre-existing session (T129738).
3278  // This is checked here for cases where people only call
3279  // $user->isAllowed(). It is also checked in Title::checkUserBlock()
3280  // to give a better error message in the common case.
3281  $config = RequestContext::getMain()->getConfig();
3282  if (
3283  $this->isLoggedIn() &&
3284  $config->get( 'BlockDisablesLogin' ) &&
3285  $this->isBlocked()
3286  ) {
3287  $anon = new User;
3288  $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
3289  }
3290  }
3291  return $this->mRights;
3292  }
3293 
3299  public function getGroups() {
3300  $this->load();
3301  $this->loadGroups();
3302  return array_keys( $this->mGroupMemberships );
3303  }
3304 
3312  public function getGroupMemberships() {
3313  $this->load();
3314  $this->loadGroups();
3315  return $this->mGroupMemberships;
3316  }
3317 
3325  public function getEffectiveGroups( $recache = false ) {
3326  if ( $recache || is_null( $this->mEffectiveGroups ) ) {
3327  $this->mEffectiveGroups = array_unique( array_merge(
3328  $this->getGroups(), // explicit groups
3329  $this->getAutomaticGroups( $recache ) // implicit groups
3330  ) );
3331  // Avoid PHP 7.1 warning of passing $this by reference
3332  $user = $this;
3333  // Hook for additional groups
3334  Hooks::run( 'UserEffectiveGroups', [ &$user, &$this->mEffectiveGroups ] );
3335  // Force reindexation of groups when a hook has unset one of them
3336  $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
3337  }
3338  return $this->mEffectiveGroups;
3339  }
3340 
3348  public function getAutomaticGroups( $recache = false ) {
3349  if ( $recache || is_null( $this->mImplicitGroups ) ) {
3350  $this->mImplicitGroups = [ '*' ];
3351  if ( $this->getId() ) {
3352  $this->mImplicitGroups[] = 'user';
3353 
3354  $this->mImplicitGroups = array_unique( array_merge(
3355  $this->mImplicitGroups,
3357  ) );
3358  }
3359  if ( $recache ) {
3360  // Assure data consistency with rights/groups,
3361  // as getEffectiveGroups() depends on this function
3362  $this->mEffectiveGroups = null;
3363  }
3364  }
3365  return $this->mImplicitGroups;
3366  }
3367 
3377  public function getFormerGroups() {
3378  $this->load();
3379 
3380  if ( is_null( $this->mFormerGroups ) ) {
3381  $db = ( $this->queryFlagsUsed & self::READ_LATEST )
3382  ? wfGetDB( DB_MASTER )
3383  : wfGetDB( DB_REPLICA );
3384  $res = $db->select( 'user_former_groups',
3385  [ 'ufg_group' ],
3386  [ 'ufg_user' => $this->mId ],
3387  __METHOD__ );
3388  $this->mFormerGroups = [];
3389  foreach ( $res as $row ) {
3390  $this->mFormerGroups[] = $row->ufg_group;
3391  }
3392  }
3393 
3394  return $this->mFormerGroups;
3395  }
3396 
3401  public function getEditCount() {
3402  if ( !$this->getId() ) {
3403  return null;
3404  }
3405 
3406  if ( $this->mEditCount === null ) {
3407  /* Populate the count, if it has not been populated yet */
3408  $dbr = wfGetDB( DB_REPLICA );
3409  // check if the user_editcount field has been initialized
3410  $count = $dbr->selectField(
3411  'user', 'user_editcount',
3412  [ 'user_id' => $this->mId ],
3413  __METHOD__
3414  );
3415 
3416  if ( $count === null ) {
3417  // it has not been initialized. do so.
3418  $count = $this->initEditCount();
3419  }
3420  $this->mEditCount = $count;
3421  }
3422  return (int)$this->mEditCount;
3423  }
3424 
3436  public function addGroup( $group, $expiry = null ) {
3437  $this->load();
3438  $this->loadGroups();
3439 
3440  if ( $expiry ) {
3441  $expiry = wfTimestamp( TS_MW, $expiry );
3442  }
3443 
3444  if ( !Hooks::run( 'UserAddGroup', [ $this, &$group, &$expiry ] ) ) {
3445  return false;
3446  }
3447 
3448  // create the new UserGroupMembership and put it in the DB
3449  $ugm = new UserGroupMembership( $this->mId, $group, $expiry );
3450  if ( !$ugm->insert( true ) ) {
3451  return false;
3452  }
3453 
3454  $this->mGroupMemberships[$group] = $ugm;
3455 
3456  // Refresh the groups caches, and clear the rights cache so it will be
3457  // refreshed on the next call to $this->getRights().
3458  $this->getEffectiveGroups( true );
3459  $this->mRights = null;
3460 
3461  $this->invalidateCache();
3462 
3463  return true;
3464  }
3465 
3472  public function removeGroup( $group ) {
3473  $this->load();
3474 
3475  if ( !Hooks::run( 'UserRemoveGroup', [ $this, &$group ] ) ) {
3476  return false;
3477  }
3478 
3479  $ugm = UserGroupMembership::getMembership( $this->mId, $group );
3480  // delete the membership entry
3481  if ( !$ugm || !$ugm->delete() ) {
3482  return false;
3483  }
3484 
3485  $this->loadGroups();
3486  unset( $this->mGroupMemberships[$group] );
3487 
3488  // Refresh the groups caches, and clear the rights cache so it will be
3489  // refreshed on the next call to $this->getRights().
3490  $this->getEffectiveGroups( true );
3491  $this->mRights = null;
3492 
3493  $this->invalidateCache();
3494 
3495  return true;
3496  }
3497 
3502  public function isLoggedIn() {
3503  return $this->getId() != 0;
3504  }
3505 
3510  public function isAnon() {
3511  return !$this->isLoggedIn();
3512  }
3513 
3518  public function isBot() {
3519  if ( in_array( 'bot', $this->getGroups() ) && $this->isAllowed( 'bot' ) ) {
3520  return true;
3521  }
3522 
3523  $isBot = false;
3524  Hooks::run( "UserIsBot", [ $this, &$isBot ] );
3525 
3526  return $isBot;
3527  }
3528 
3535  public function isAllowedAny() {
3536  $permissions = func_get_args();
3537  foreach ( $permissions as $permission ) {
3538  if ( $this->isAllowed( $permission ) ) {
3539  return true;
3540  }
3541  }
3542  return false;
3543  }
3544 
3550  public function isAllowedAll() {
3551  $permissions = func_get_args();
3552  foreach ( $permissions as $permission ) {
3553  if ( !$this->isAllowed( $permission ) ) {
3554  return false;
3555  }
3556  }
3557  return true;
3558  }
3559 
3565  public function isAllowed( $action = '' ) {
3566  if ( $action === '' ) {
3567  return true; // In the spirit of DWIM
3568  }
3569  // Use strict parameter to avoid matching numeric 0 accidentally inserted
3570  // by misconfiguration: 0 == 'foo'
3571  return in_array( $action, $this->getRights(), true );
3572  }
3573 
3578  public function useRCPatrol() {
3580  return $wgUseRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
3581  }
3582 
3587  public function useNPPatrol() {
3589  return (
3590  ( $wgUseRCPatrol || $wgUseNPPatrol )
3591  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3592  );
3593  }
3594 
3599  public function useFilePatrol() {
3601  return (
3602  ( $wgUseRCPatrol || $wgUseFilePatrol )
3603  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3604  );
3605  }
3606 
3612  public function getRequest() {
3613  if ( $this->mRequest ) {
3614  return $this->mRequest;
3615  } else {
3617  return $wgRequest;
3618  }
3619  }
3620 
3629  public function isWatched( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3630  if ( $title->isWatchable() && ( !$checkRights || $this->isAllowed( 'viewmywatchlist' ) ) ) {
3631  return MediaWikiServices::getInstance()->getWatchedItemStore()->isWatched( $this, $title );
3632  }
3633  return false;
3634  }
3635 
3643  public function addWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3644  if ( !$checkRights || $this->isAllowed( 'editmywatchlist' ) ) {
3645  MediaWikiServices::getInstance()->getWatchedItemStore()->addWatchBatchForUser(
3646  $this,
3647  [ $title->getSubjectPage(), $title->getTalkPage() ]
3648  );
3649  }
3650  $this->invalidateCache();
3651  }
3652 
3660  public function removeWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3661  if ( !$checkRights || $this->isAllowed( 'editmywatchlist' ) ) {
3662  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3663  $store->removeWatch( $this, $title->getSubjectPage() );
3664  $store->removeWatch( $this, $title->getTalkPage() );
3665  }
3666  $this->invalidateCache();
3667  }
3668 
3677  public function clearNotification( &$title, $oldid = 0 ) {
3679 
3680  // Do nothing if the database is locked to writes
3681  if ( wfReadOnly() ) {
3682  return;
3683  }
3684 
3685  // Do nothing if not allowed to edit the watchlist
3686  if ( !$this->isAllowed( 'editmywatchlist' ) ) {
3687  return;
3688  }
3689 
3690  // If we're working on user's talk page, we should update the talk page message indicator
3691  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3692  // Avoid PHP 7.1 warning of passing $this by reference
3693  $user = $this;
3694  if ( !Hooks::run( 'UserClearNewTalkNotification', [ &$user, $oldid ] ) ) {
3695  return;
3696  }
3697 
3698  // Try to update the DB post-send and only if needed...
3699  DeferredUpdates::addCallableUpdate( function () use ( $title, $oldid ) {
3700  if ( !$this->getNewtalk() ) {
3701  return; // no notifications to clear
3702  }
3703 
3704  // Delete the last notifications (they stack up)
3705  $this->setNewtalk( false );
3706 
3707  // If there is a new, unseen, revision, use its timestamp
3708  $nextid = $oldid
3709  ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
3710  : null;
3711  if ( $nextid ) {
3712  $this->setNewtalk( true, Revision::newFromId( $nextid ) );
3713  }
3714  } );
3715  }
3716 
3717  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3718  return;
3719  }
3720 
3721  if ( $this->isAnon() ) {
3722  // Nothing else to do...
3723  return;
3724  }
3725 
3726  // Only update the timestamp if the page is being watched.
3727  // The query to find out if it is watched is cached both in memcached and per-invocation,
3728  // and when it does have to be executed, it can be on a replica DB
3729  // If this is the user's newtalk page, we always update the timestamp
3730  $force = '';
3731  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3732  $force = 'force';
3733  }
3734 
3735  MediaWikiServices::getInstance()->getWatchedItemStore()
3736  ->resetNotificationTimestamp( $this, $title, $force, $oldid );
3737  }
3738 
3745  public function clearAllNotifications() {
3747  // Do nothing if not allowed to edit the watchlist
3748  if ( wfReadOnly() || !$this->isAllowed( 'editmywatchlist' ) ) {
3749  return;
3750  }
3751 
3752  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3753  $this->setNewtalk( false );
3754  return;
3755  }
3756 
3757  $id = $this->getId();
3758  if ( !$id ) {
3759  return;
3760  }
3761 
3762  $dbw = wfGetDB( DB_MASTER );
3763  $asOfTimes = array_unique( $dbw->selectFieldValues(
3764  'watchlist',
3765  'wl_notificationtimestamp',
3766  [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
3767  __METHOD__,
3768  [ 'ORDER BY' => 'wl_notificationtimestamp DESC', 'LIMIT' => 500 ]
3769  ) );
3770  if ( !$asOfTimes ) {
3771  return;
3772  }
3773  // Immediately update the most recent touched rows, which hopefully covers what
3774  // the user sees on the watchlist page before pressing "mark all pages visited"....
3775  $dbw->update(
3776  'watchlist',
3777  [ 'wl_notificationtimestamp' => null ],
3778  [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimes ],
3779  __METHOD__
3780  );
3781  // ...and finish the older ones in a post-send update with lag checks...
3783  $dbw,
3784  __METHOD__,
3785  function () use ( $dbw, $id ) {
3787 
3788  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3789  $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
3790  $asOfTimes = array_unique( $dbw->selectFieldValues(
3791  'watchlist',
3792  'wl_notificationtimestamp',
3793  [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
3794  __METHOD__
3795  ) );
3796  foreach ( array_chunk( $asOfTimes, $wgUpdateRowsPerQuery ) as $asOfTimeBatch ) {
3797  $dbw->update(
3798  'watchlist',
3799  [ 'wl_notificationtimestamp' => null ],
3800  [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimeBatch ],
3801  __METHOD__
3802  );
3803  $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
3804  }
3805  }
3806  ) );
3807  // We also need to clear here the "you have new message" notification for the own
3808  // user_talk page; it's cleared one page view later in WikiPage::doViewUpdates().
3809  }
3810 
3816  public function getExperienceLevel() {
3821 
3822  if ( $this->isAnon() ) {
3823  return false;
3824  }
3825 
3826  $editCount = $this->getEditCount();
3827  $registration = $this->getRegistration();
3828  $now = time();
3829  $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
3830  $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
3831 
3832  if (
3833  $editCount < $wgLearnerEdits ||
3834  $registration > $learnerRegistration
3835  ) {
3836  return 'newcomer';
3837  } elseif (
3838  $editCount > $wgExperiencedUserEdits &&
3839  $registration <= $experiencedRegistration
3840  ) {
3841  return 'experienced';
3842  } else {
3843  return 'learner';
3844  }
3845  }
3846 
3863  protected function setCookie(
3864  $name, $value, $exp = 0, $secure = null, $params = [], $request = null
3865  ) {
3866  wfDeprecated( __METHOD__, '1.27' );
3867  if ( $request === null ) {
3868  $request = $this->getRequest();
3869  }
3870  $params['secure'] = $secure;
3871  $request->response()->setCookie( $name, $value, $exp, $params );
3872  }
3873 
3884  protected function clearCookie( $name, $secure = null, $params = [] ) {
3885  wfDeprecated( __METHOD__, '1.27' );
3886  $this->setCookie( $name, '', time() - 86400, $secure, $params );
3887  }
3888 
3904  protected function setExtendedLoginCookie( $name, $value, $secure ) {
3906 
3907  wfDeprecated( __METHOD__, '1.27' );
3908 
3909  $exp = time();
3910  $exp += $wgExtendedLoginCookieExpiration !== null
3913 
3914  $this->setCookie( $name, $value, $exp, $secure );
3915  }
3916 
3925  public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
3926  $this->load();
3927  if ( 0 == $this->mId ) {
3928  return;
3929  }
3930 
3931  $session = $this->getRequest()->getSession();
3932  if ( $request && $session->getRequest() !== $request ) {
3933  $session = $session->sessionWithRequest( $request );
3934  }
3935  $delay = $session->delaySave();
3936 
3937  if ( !$session->getUser()->equals( $this ) ) {
3938  if ( !$session->canSetUser() ) {
3940  ->warning( __METHOD__ .
3941  ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3942  );
3943  return;
3944  }
3945  $session->setUser( $this );
3946  }
3947 
3948  $session->setRememberUser( $rememberMe );
3949  if ( $secure !== null ) {
3950  $session->setForceHTTPS( $secure );
3951  }
3952 
3953  $session->persist();
3954 
3955  ScopedCallback::consume( $delay );
3956  }
3957 
3961  public function logout() {
3962  // Avoid PHP 7.1 warning of passing $this by reference
3963  $user = $this;
3964  if ( Hooks::run( 'UserLogout', [ &$user ] ) ) {
3965  $this->doLogout();
3966  }
3967  }
3968 
3973  public function doLogout() {
3974  $session = $this->getRequest()->getSession();
3975  if ( !$session->canSetUser() ) {
3977  ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
3978  $error = 'immutable';
3979  } elseif ( !$session->getUser()->equals( $this ) ) {
3981  ->warning( __METHOD__ .
3982  ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
3983  );
3984  // But we still may as well make this user object anon
3985  $this->clearInstanceCache( 'defaults' );
3986  $error = 'wronguser';
3987  } else {
3988  $this->clearInstanceCache( 'defaults' );
3989  $delay = $session->delaySave();
3990  $session->unpersist(); // Clear cookies (T127436)
3991  $session->setLoggedOutTimestamp( time() );
3992  $session->setUser( new User );
3993  $session->set( 'wsUserID', 0 ); // Other code expects this
3994  $session->resetAllTokens();
3995  ScopedCallback::consume( $delay );
3996  $error = false;
3997  }
3998  \MediaWiki\Logger\LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
3999  'event' => 'logout',
4000  'successful' => $error === false,
4001  'status' => $error ?: 'success',
4002  ] );
4003  }
4004 
4009  public function saveSettings() {
4010  if ( wfReadOnly() ) {
4011  // @TODO: caller should deal with this instead!
4012  // This should really just be an exception.
4014  null,
4015  "Could not update user with ID '{$this->mId}'; DB is read-only."
4016  ) );
4017  return;
4018  }
4019 
4020  $this->load();
4021  if ( 0 == $this->mId ) {
4022  return; // anon
4023  }
4024 
4025  // Get a new user_touched that is higher than the old one.
4026  // This will be used for a CAS check as a last-resort safety
4027  // check against race conditions and replica DB lag.
4028  $newTouched = $this->newTouchedTimestamp();
4029 
4030  $dbw = wfGetDB( DB_MASTER );
4031  $dbw->update( 'user',
4032  [ /* SET */
4033  'user_name' => $this->mName,
4034  'user_real_name' => $this->mRealName,
4035  'user_email' => $this->mEmail,
4036  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
4037  'user_touched' => $dbw->timestamp( $newTouched ),
4038  'user_token' => strval( $this->mToken ),
4039  'user_email_token' => $this->mEmailToken,
4040  'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
4041  ], $this->makeUpdateConditions( $dbw, [ /* WHERE */
4042  'user_id' => $this->mId,
4043  ] ), __METHOD__
4044  );
4045 
4046  if ( !$dbw->affectedRows() ) {
4047  // Maybe the problem was a missed cache update; clear it to be safe
4048  $this->clearSharedCache( 'refresh' );
4049  // User was changed in the meantime or loaded with stale data
4050  $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
4051  throw new MWException(
4052  "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
4053  " the version of the user to be saved is older than the current version."
4054  );
4055  }
4056 
4057  $this->mTouched = $newTouched;
4058  $this->saveOptions();
4059 
4060  Hooks::run( 'UserSaveSettings', [ $this ] );
4061  $this->clearSharedCache();
4062  $this->getUserPage()->invalidateCache();
4063  }
4064 
4071  public function idForName( $flags = 0 ) {
4072  $s = trim( $this->getName() );
4073  if ( $s === '' ) {
4074  return 0;
4075  }
4076 
4077  $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
4078  ? wfGetDB( DB_MASTER )
4079  : wfGetDB( DB_REPLICA );
4080 
4081  $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
4082  ? [ 'LOCK IN SHARE MODE' ]
4083  : [];
4084 
4085  $id = $db->selectField( 'user',
4086  'user_id', [ 'user_name' => $s ], __METHOD__, $options );
4087 
4088  return (int)$id;
4089  }
4090 
4106  public static function createNew( $name, $params = [] ) {
4107  foreach ( [ 'password', 'newpassword', 'newpass_time', 'password_expires' ] as $field ) {
4108  if ( isset( $params[$field] ) ) {
4109  wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
4110  unset( $params[$field] );
4111  }
4112  }
4113 
4114  $user = new User;
4115  $user->load();
4116  $user->setToken(); // init token
4117  if ( isset( $params['options'] ) ) {
4118  $user->mOptions = $params['options'] + (array)$user->mOptions;
4119  unset( $params['options'] );
4120  }
4121  $dbw = wfGetDB( DB_MASTER );
4122 
4123  $noPass = PasswordFactory::newInvalidPassword()->toString();
4124 
4125  $fields = [
4126  'user_name' => $name,
4127  'user_password' => $noPass,
4128  'user_newpassword' => $noPass,
4129  'user_email' => $user->mEmail,
4130  'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
4131  'user_real_name' => $user->mRealName,
4132  'user_token' => strval( $user->mToken ),
4133  'user_registration' => $dbw->timestamp( $user->mRegistration ),
4134  'user_editcount' => 0,
4135  'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
4136  ];
4137  foreach ( $params as $name => $value ) {
4138  $fields["user_$name"] = $value;
4139  }
4140  $dbw->insert( 'user', $fields, __METHOD__, [ 'IGNORE' ] );
4141  if ( $dbw->affectedRows() ) {
4142  $newUser = self::newFromId( $dbw->insertId() );
4143  } else {
4144  $newUser = null;
4145  }
4146  return $newUser;
4147  }
4148 
4175  public function addToDatabase() {
4176  $this->load();
4177  if ( !$this->mToken ) {
4178  $this->setToken(); // init token
4179  }
4180 
4181  if ( !is_string( $this->mName ) ) {
4182  throw new RuntimeException( "User name field is not set." );
4183  }
4184 
4185  $this->mTouched = $this->newTouchedTimestamp();
4186 
4187  $noPass = PasswordFactory::newInvalidPassword()->toString();
4188 
4189  $dbw = wfGetDB( DB_MASTER );
4190  $dbw->insert( 'user',
4191  [
4192  'user_name' => $this->mName,
4193  'user_password' => $noPass,
4194  'user_newpassword' => $noPass,
4195  'user_email' => $this->mEmail,
4196  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
4197  'user_real_name' => $this->mRealName,
4198  'user_token' => strval( $this->mToken ),
4199  'user_registration' => $dbw->timestamp( $this->mRegistration ),
4200  'user_editcount' => 0,
4201  'user_touched' => $dbw->timestamp( $this->mTouched ),
4202  ], __METHOD__,
4203  [ 'IGNORE' ]
4204  );
4205  if ( !$dbw->affectedRows() ) {
4206  // Use locking reads to bypass any REPEATABLE-READ snapshot.
4207  $this->mId = $dbw->selectField(
4208  'user',
4209  'user_id',
4210  [ 'user_name' => $this->mName ],
4211  __METHOD__,
4212  [ 'LOCK IN SHARE MODE' ]
4213  );
4214  $loaded = false;
4215  if ( $this->mId ) {
4216  if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
4217  $loaded = true;
4218  }
4219  }
4220  if ( !$loaded ) {
4221  throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
4222  "to insert user '{$this->mName}' row, but it was not present in select!" );
4223  }
4224  return Status::newFatal( 'userexists' );
4225  }
4226  $this->mId = $dbw->insertId();
4227  self::$idCacheByName[$this->mName] = $this->mId;
4228 
4229  // Clear instance cache other than user table data, which is already accurate
4230  $this->clearInstanceCache();
4231 
4232  $this->saveOptions();
4233  return Status::newGood();
4234  }
4235 
4241  public function spreadAnyEditBlock() {
4242  if ( $this->isLoggedIn() && $this->isBlocked() ) {
4243  return $this->spreadBlock();
4244  }
4245 
4246  return false;
4247  }
4248 
4254  protected function spreadBlock() {
4255  wfDebug( __METHOD__ . "()\n" );
4256  $this->load();
4257  if ( $this->mId == 0 ) {
4258  return false;
4259  }
4260 
4261  $userblock = Block::newFromTarget( $this->getName() );
4262  if ( !$userblock ) {
4263  return false;
4264  }
4265 
4266  return (bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
4267  }
4268 
4273  public function isBlockedFromCreateAccount() {
4274  $this->getBlockedStatus();
4275  if ( $this->mBlock && $this->mBlock->prevents( 'createaccount' ) ) {
4276  return $this->mBlock;
4277  }
4278 
4279  # T15611: if the IP address the user is trying to create an account from is
4280  # blocked with createaccount disabled, prevent new account creation there even
4281  # when the user is logged in
4282  if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
4283  $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
4284  }
4285  return $this->mBlockedFromCreateAccount instanceof Block
4286  && $this->mBlockedFromCreateAccount->prevents( 'createaccount' )
4287  ? $this->mBlockedFromCreateAccount
4288  : false;
4289  }
4290 
4295  public function isBlockedFromEmailuser() {
4296  $this->getBlockedStatus();
4297  return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
4298  }
4299 
4304  public function isAllowedToCreateAccount() {
4305  return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
4306  }
4307 
4313  public function getUserPage() {
4314  return Title::makeTitle( NS_USER, $this->getName() );
4315  }
4316 
4322  public function getTalkPage() {
4323  $title = $this->getUserPage();
4324  return $title->getTalkPage();
4325  }
4326 
4332  public function isNewbie() {
4333  return !$this->isAllowed( 'autoconfirmed' );
4334  }
4335 
4342  public function checkPassword( $password ) {
4343  $manager = AuthManager::singleton();
4344  $reqs = AuthenticationRequest::loadRequestsFromSubmission(
4345  $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
4346  [
4347  'username' => $this->getName(),
4348  'password' => $password,
4349  ]
4350  );
4351  $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
4352  switch ( $res->status ) {
4353  case AuthenticationResponse::PASS:
4354  return true;
4355  case AuthenticationResponse::FAIL:
4356  // Hope it's not a PreAuthenticationProvider that failed...
4358  ->info( __METHOD__ . ': Authentication failed: ' . $res->message->plain() );
4359  return false;
4360  default:
4361  throw new BadMethodCallException(
4362  'AuthManager returned a response unsupported by ' . __METHOD__
4363  );
4364  }
4365  }
4366 
4375  public function checkTemporaryPassword( $plaintext ) {
4376  // Can't check the temporary password individually.
4377  return $this->checkPassword( $plaintext );
4378  }
4379 
4391  public function getEditTokenObject( $salt = '', $request = null ) {
4392  if ( $this->isAnon() ) {
4393  return new LoggedOutEditToken();
4394  }
4395 
4396  if ( !$request ) {
4397  $request = $this->getRequest();
4398  }
4399  return $request->getSession()->getToken( $salt );
4400  }
4401 
4415  public function getEditToken( $salt = '', $request = null ) {
4416  return $this->getEditTokenObject( $salt, $request )->toString();
4417  }
4418 
4425  public static function getEditTokenTimestamp( $val ) {
4426  wfDeprecated( __METHOD__, '1.27' );
4428  }
4429 
4442  public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
4443  return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
4444  }
4445 
4456  public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
4457  $val = substr( $val, 0, strspn( $val, '0123456789abcdef' ) ) . Token::SUFFIX;
4458  return $this->matchEditToken( $val, $salt, $request, $maxage );
4459  }
4460 
4468  public function sendConfirmationMail( $type = 'created' ) {
4469  global $wgLang;
4470  $expiration = null; // gets passed-by-ref and defined in next line.
4471  $token = $this->confirmationToken( $expiration );
4472  $url = $this->confirmationTokenUrl( $token );
4473  $invalidateURL = $this->invalidationTokenUrl( $token );
4474  $this->saveSettings();
4475 
4476  if ( $type == 'created' || $type === false ) {
4477  $message = 'confirmemail_body';
4478  } elseif ( $type === true ) {
4479  $message = 'confirmemail_body_changed';
4480  } else {
4481  // Messages: confirmemail_body_changed, confirmemail_body_set
4482  $message = 'confirmemail_body_' . $type;
4483  }
4484 
4485  return $this->sendMail( wfMessage( 'confirmemail_subject' )->text(),
4486  wfMessage( $message,
4487  $this->getRequest()->getIP(),
4488  $this->getName(),
4489  $url,
4490  $wgLang->userTimeAndDate( $expiration, $this ),
4491  $invalidateURL,
4492  $wgLang->userDate( $expiration, $this ),
4493  $wgLang->userTime( $expiration, $this ) )->text() );
4494  }
4495 
4507  public function sendMail( $subject, $body, $from = null, $replyto = null ) {
4509 
4510  if ( $from instanceof User ) {
4511  $sender = MailAddress::newFromUser( $from );
4512  } else {
4513  $sender = new MailAddress( $wgPasswordSender,
4514  wfMessage( 'emailsender' )->inContentLanguage()->text() );
4515  }
4516  $to = MailAddress::newFromUser( $this );
4517 
4518  return UserMailer::send( $to, $sender, $subject, $body, [
4519  'replyTo' => $replyto,
4520  ] );
4521  }
4522 
4533  protected function confirmationToken( &$expiration ) {
4535  $now = time();
4536  $expires = $now + $wgUserEmailConfirmationTokenExpiry;
4537  $expiration = wfTimestamp( TS_MW, $expires );
4538  $this->load();
4539  $token = MWCryptRand::generateHex( 32 );
4540  $hash = md5( $token );
4541  $this->mEmailToken = $hash;
4542  $this->mEmailTokenExpires = $expiration;
4543  return $token;
4544  }
4545 
4551  protected function confirmationTokenUrl( $token ) {
4552  return $this->getTokenUrl( 'ConfirmEmail', $token );
4553  }
4554 
4560  protected function invalidationTokenUrl( $token ) {
4561  return $this->getTokenUrl( 'InvalidateEmail', $token );
4562  }
4563 
4578  protected function getTokenUrl( $page, $token ) {
4579  // Hack to bypass localization of 'Special:'
4580  $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
4581  return $title->getCanonicalURL();
4582  }
4583 
4591  public function confirmEmail() {
4592  // Check if it's already confirmed, so we don't touch the database
4593  // and fire the ConfirmEmailComplete hook on redundant confirmations.
4594  if ( !$this->isEmailConfirmed() ) {
4596  Hooks::run( 'ConfirmEmailComplete', [ $this ] );
4597  }
4598  return true;
4599  }
4600 
4608  public function invalidateEmail() {
4609  $this->load();
4610  $this->mEmailToken = null;
4611  $this->mEmailTokenExpires = null;
4612  $this->setEmailAuthenticationTimestamp( null );
4613  $this->mEmail = '';
4614  Hooks::run( 'InvalidateEmailComplete', [ $this ] );
4615  return true;
4616  }
4617 
4622  public function setEmailAuthenticationTimestamp( $timestamp ) {
4623  $this->load();
4624  $this->mEmailAuthenticated = $timestamp;
4625  Hooks::run( 'UserSetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
4626  }
4627 
4633  public function canSendEmail() {
4635  if ( !$wgEnableEmail || !$wgEnableUserEmail || !$this->isAllowed( 'sendemail' ) ) {
4636  return false;
4637  }
4638  $canSend = $this->isEmailConfirmed();
4639  // Avoid PHP 7.1 warning of passing $this by reference
4640  $user = $this;
4641  Hooks::run( 'UserCanSendEmail', [ &$user, &$canSend ] );
4642  return $canSend;
4643  }
4644 
4650  public function canReceiveEmail() {
4651  return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
4652  }
4653 
4664  public function isEmailConfirmed() {
4666  $this->load();
4667  // Avoid PHP 7.1 warning of passing $this by reference
4668  $user = $this;
4669  $confirmed = true;
4670  if ( Hooks::run( 'EmailConfirmed', [ &$user, &$confirmed ] ) ) {
4671  if ( $this->isAnon() ) {
4672  return false;
4673  }
4674  if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4675  return false;
4676  }
4678  return false;
4679  }
4680  return true;
4681  } else {
4682  return $confirmed;
4683  }
4684  }
4685 
4690  public function isEmailConfirmationPending() {
4692  return $wgEmailAuthentication &&
4693  !$this->isEmailConfirmed() &&
4694  $this->mEmailToken &&
4695  $this->mEmailTokenExpires > wfTimestamp();
4696  }
4697 
4705  public function getRegistration() {
4706  if ( $this->isAnon() ) {
4707  return false;
4708  }
4709  $this->load();
4710  return $this->mRegistration;
4711  }
4712 
4719  public function getFirstEditTimestamp() {
4720  if ( $this->getId() == 0 ) {
4721  return false; // anons
4722  }
4723  $dbr = wfGetDB( DB_REPLICA );
4724  $time = $dbr->selectField( 'revision', 'rev_timestamp',
4725  [ 'rev_user' => $this->getId() ],
4726  __METHOD__,
4727  [ 'ORDER BY' => 'rev_timestamp ASC' ]
4728  );
4729  if ( !$time ) {
4730  return false; // no edits
4731  }
4732  return wfTimestamp( TS_MW, $time );
4733  }
4734 
4741  public static function getGroupPermissions( $groups ) {
4743  $rights = [];
4744  // grant every granted permission first
4745  foreach ( $groups as $group ) {
4746  if ( isset( $wgGroupPermissions[$group] ) ) {
4747  $rights = array_merge( $rights,
4748  // array_filter removes empty items
4749  array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
4750  }
4751  }
4752  // now revoke the revoked permissions
4753  foreach ( $groups as $group ) {
4754  if ( isset( $wgRevokePermissions[$group] ) ) {
4755  $rights = array_diff( $rights,
4756  array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
4757  }
4758  }
4759  return array_unique( $rights );
4760  }
4761 
4768  public static function getGroupsWithPermission( $role ) {
4770  $allowedGroups = [];
4771  foreach ( array_keys( $wgGroupPermissions ) as $group ) {
4772  if ( self::groupHasPermission( $group, $role ) ) {
4773  $allowedGroups[] = $group;
4774  }
4775  }
4776  return $allowedGroups;
4777  }
4778 
4791  public static function groupHasPermission( $group, $role ) {
4793  return isset( $wgGroupPermissions[$group][$role] ) && $wgGroupPermissions[$group][$role]
4794  && !( isset( $wgRevokePermissions[$group][$role] ) && $wgRevokePermissions[$group][$role] );
4795  }
4796 
4811  public static function isEveryoneAllowed( $right ) {
4813  static $cache = [];
4814 
4815  // Use the cached results, except in unit tests which rely on
4816  // being able change the permission mid-request
4817  if ( isset( $cache[$right] ) && !defined( 'MW_PHPUNIT_TEST' ) ) {
4818  return $cache[$right];
4819  }
4820 
4821  if ( !isset( $wgGroupPermissions['*'][$right] ) || !$wgGroupPermissions['*'][$right] ) {
4822  $cache[$right] = false;
4823  return false;
4824  }
4825 
4826  // If it's revoked anywhere, then everyone doesn't have it
4827  foreach ( $wgRevokePermissions as $rights ) {
4828  if ( isset( $rights[$right] ) && $rights[$right] ) {
4829  $cache[$right] = false;
4830  return false;
4831  }
4832  }
4833 
4834  // Remove any rights that aren't allowed to the global-session user,
4835  // unless there are no sessions for this endpoint.
4836  if ( !defined( 'MW_NO_SESSION' ) ) {
4837  $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
4838  if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
4839  $cache[$right] = false;
4840  return false;
4841  }
4842  }
4843 
4844  // Allow extensions to say false
4845  if ( !Hooks::run( 'UserIsEveryoneAllowed', [ $right ] ) ) {
4846  $cache[$right] = false;
4847  return false;
4848  }
4849 
4850  $cache[$right] = true;
4851  return true;
4852  }
4853 
4861  public static function getGroupName( $group ) {
4862  wfDeprecated( __METHOD__, '1.29' );
4863  return UserGroupMembership::getGroupName( $group );
4864  }
4865 
4874  public static function getGroupMember( $group, $username = '#' ) {
4875  wfDeprecated( __METHOD__, '1.29' );
4877  }
4878 
4885  public static function getAllGroups() {
4887  return array_diff(
4888  array_merge( array_keys( $wgGroupPermissions ), array_keys( $wgRevokePermissions ) ),
4889  self::getImplicitGroups()
4890  );
4891  }
4892 
4897  public static function getAllRights() {
4898  if ( self::$mAllRights === false ) {
4900  if ( count( $wgAvailableRights ) ) {
4901  self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
4902  } else {
4903  self::$mAllRights = self::$mCoreRights;
4904  }
4905  Hooks::run( 'UserGetAllRights', [ &self::$mAllRights ] );
4906  }
4907  return self::$mAllRights;
4908  }
4909 
4914  public static function getImplicitGroups() {
4916 
4917  $groups = $wgImplicitGroups;
4918  # Deprecated, use $wgImplicitGroups instead
4919  Hooks::run( 'UserGetImplicitGroups', [ &$groups ], '1.25' );
4920 
4921  return $groups;
4922  }
4923 
4931  public static function getGroupPage( $group ) {
4932  wfDeprecated( __METHOD__, '1.29' );
4933  return UserGroupMembership::getGroupPage( $group );
4934  }
4935 
4946  public static function makeGroupLinkHTML( $group, $text = '' ) {
4947  wfDeprecated( __METHOD__, '1.29' );
4948 
4949  if ( $text == '' ) {
4950  $text = UserGroupMembership::getGroupName( $group );
4951  }
4953  if ( $title ) {
4954  return MediaWikiServices::getInstance()
4955  ->getLinkRenderer()->makeLink( $title, $text );
4956  } else {
4957  return htmlspecialchars( $text );
4958  }
4959  }
4960 
4971  public static function makeGroupLinkWiki( $group, $text = '' ) {
4972  wfDeprecated( __METHOD__, '1.29' );
4973 
4974  if ( $text == '' ) {
4975  $text = UserGroupMembership::getGroupName( $group );
4976  }
4978  if ( $title ) {
4979  $page = $title->getFullText();
4980  return "[[$page|$text]]";
4981  } else {
4982  return $text;
4983  }
4984  }
4985 
4995  public static function changeableByGroup( $group ) {
4997 
4998  $groups = [
4999  'add' => [],
5000  'remove' => [],
5001  'add-self' => [],
5002  'remove-self' => []
5003  ];
5004 
5005  if ( empty( $wgAddGroups[$group] ) ) {
5006  // Don't add anything to $groups
5007  } elseif ( $wgAddGroups[$group] === true ) {
5008  // You get everything
5009  $groups['add'] = self::getAllGroups();
5010  } elseif ( is_array( $wgAddGroups[$group] ) ) {
5011  $groups['add'] = $wgAddGroups[$group];
5012  }
5013 
5014  // Same thing for remove
5015  if ( empty( $wgRemoveGroups[$group] ) ) {
5016  // Do nothing
5017  } elseif ( $wgRemoveGroups[$group] === true ) {
5018  $groups['remove'] = self::getAllGroups();
5019  } elseif ( is_array( $wgRemoveGroups[$group] ) ) {
5020  $groups['remove'] = $wgRemoveGroups[$group];
5021  }
5022 
5023  // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
5024  if ( empty( $wgGroupsAddToSelf['user'] ) || $wgGroupsAddToSelf['user'] !== true ) {
5025  foreach ( $wgGroupsAddToSelf as $key => $value ) {
5026  if ( is_int( $key ) ) {
5027  $wgGroupsAddToSelf['user'][] = $value;
5028  }
5029  }
5030  }
5031 
5032  if ( empty( $wgGroupsRemoveFromSelf['user'] ) || $wgGroupsRemoveFromSelf['user'] !== true ) {
5033  foreach ( $wgGroupsRemoveFromSelf as $key => $value ) {
5034  if ( is_int( $key ) ) {
5035  $wgGroupsRemoveFromSelf['user'][] = $value;
5036  }
5037  }
5038  }
5039 
5040  // Now figure out what groups the user can add to him/herself
5041  if ( empty( $wgGroupsAddToSelf[$group] ) ) {
5042  // Do nothing
5043  } elseif ( $wgGroupsAddToSelf[$group] === true ) {
5044  // No idea WHY this would be used, but it's there
5045  $groups['add-self'] = self::getAllGroups();
5046  } elseif ( is_array( $wgGroupsAddToSelf[$group] ) ) {
5047  $groups['add-self'] = $wgGroupsAddToSelf[$group];
5048  }
5049 
5050  if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
5051  // Do nothing
5052  } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
5053  $groups['remove-self'] = self::getAllGroups();
5054  } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
5055  $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
5056  }
5057 
5058  return $groups;
5059  }
5060 
5068  public function changeableGroups() {
5069  if ( $this->isAllowed( 'userrights' ) ) {
5070  // This group gives the right to modify everything (reverse-
5071  // compatibility with old "userrights lets you change
5072  // everything")
5073  // Using array_merge to make the groups reindexed
5074  $all = array_merge( self::getAllGroups() );
5075  return [
5076  'add' => $all,
5077  'remove' => $all,
5078  'add-self' => [],
5079  'remove-self' => []
5080  ];
5081  }
5082 
5083  // Okay, it's not so simple, we will have to go through the arrays
5084  $groups = [
5085  'add' => [],
5086  'remove' => [],
5087  'add-self' => [],
5088  'remove-self' => []
5089  ];
5090  $addergroups = $this->getEffectiveGroups();
5091 
5092  foreach ( $addergroups as $addergroup ) {
5093  $groups = array_merge_recursive(
5094  $groups, $this->changeableByGroup( $addergroup )
5095  );
5096  $groups['add'] = array_unique( $groups['add'] );
5097  $groups['remove'] = array_unique( $groups['remove'] );
5098  $groups['add-self'] = array_unique( $groups['add-self'] );
5099  $groups['remove-self'] = array_unique( $groups['remove-self'] );
5100  }
5101  return $groups;
5102  }
5103 
5110  public function incEditCount() {
5111  wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
5112  function () {
5113  $this->incEditCountImmediate();
5114  },
5115  __METHOD__
5116  );
5117  }
5118 
5124  public function incEditCountImmediate() {
5125  if ( $this->isAnon() ) {
5126  return;
5127  }
5128 
5129  $dbw = wfGetDB( DB_MASTER );
5130  // No rows will be "affected" if user_editcount is NULL
5131  $dbw->update(
5132  'user',
5133  [ 'user_editcount=user_editcount+1' ],
5134  [ 'user_id' => $this->getId(), 'user_editcount IS NOT NULL' ],
5135  __METHOD__
5136  );
5137  // Lazy initialization check...
5138  if ( $dbw->affectedRows() == 0 ) {
5139  // Now here's a goddamn hack...
5140  $dbr = wfGetDB( DB_REPLICA );
5141  if ( $dbr !== $dbw ) {
5142  // If we actually have a replica DB server, the count is
5143  // at least one behind because the current transaction
5144  // has not been committed and replicated.
5145  $this->mEditCount = $this->initEditCount( 1 );
5146  } else {
5147  // But if DB_REPLICA is selecting the master, then the
5148  // count we just read includes the revision that was
5149  // just added in the working transaction.
5150  $this->mEditCount = $this->initEditCount();
5151  }
5152  } else {
5153  if ( $this->mEditCount === null ) {
5154  $this->getEditCount();
5155  $dbr = wfGetDB( DB_REPLICA );
5156  $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
5157  } else {
5158  $this->mEditCount++;
5159  }
5160  }
5161  // Edit count in user cache too
5162  $this->invalidateCache();
5163  }
5164 
5171  protected function initEditCount( $add = 0 ) {
5172  // Pull from a replica DB to be less cruel to servers
5173  // Accuracy isn't the point anyway here
5174  $dbr = wfGetDB( DB_REPLICA );
5175  $count = (int)$dbr->selectField(
5176  'revision',
5177  'COUNT(rev_user)',
5178  [ 'rev_user' => $this->getId() ],
5179  __METHOD__
5180  );
5181  $count = $count + $add;
5182 
5183  $dbw = wfGetDB( DB_MASTER );
5184  $dbw->update(
5185  'user',
5186  [ 'user_editcount' => $count ],
5187  [ 'user_id' => $this->getId() ],
5188  __METHOD__
5189  );
5190 
5191  return $count;
5192  }
5193 
5201  public static function getRightDescription( $right ) {
5202  $key = "right-$right";
5203  $msg = wfMessage( $key );
5204  return $msg->isDisabled() ? $right : $msg->text();
5205  }
5206 
5214  public static function getGrantName( $grant ) {
5215  $key = "grant-$grant";
5216  $msg = wfMessage( $key );
5217  return $msg->isDisabled() ? $grant : $msg->text();
5218  }
5219 
5240  public function addNewUserLogEntry( $action = false, $reason = '' ) {
5241  return true; // disabled
5242  }
5243 
5252  public function addNewUserLogEntryAutoCreate() {
5253  $this->addNewUserLogEntry( 'autocreate' );
5254 
5255  return true;
5256  }
5257 
5263  protected function loadOptions( $data = null ) {
5265 
5266  $this->load();
5267 
5268  if ( $this->mOptionsLoaded ) {
5269  return;
5270  }
5271 
5272  $this->mOptions = self::getDefaultOptions();
5273 
5274  if ( !$this->getId() ) {
5275  // For unlogged-in users, load language/variant options from request.
5276  // There's no need to do it for logged-in users: they can set preferences,
5277  // and handling of page content is done by $pageLang->getPreferredVariant() and such,
5278  // so don't override user's choice (especially when the user chooses site default).
5279  $variant = $wgContLang->getDefaultVariant();
5280  $this->mOptions['variant'] = $variant;
5281  $this->mOptions['language'] = $variant;
5282  $this->mOptionsLoaded = true;
5283  return;
5284  }
5285 
5286  // Maybe load from the object
5287  if ( !is_null( $this->mOptionOverrides ) ) {
5288  wfDebug( "User: loading options for user " . $this->getId() . " from override cache.\n" );
5289  foreach ( $this->mOptionOverrides as $key => $value ) {
5290  $this->mOptions[$key] = $value;
5291  }
5292  } else {
5293  if ( !is_array( $data ) ) {
5294  wfDebug( "User: loading options for user " . $this->getId() . " from database.\n" );
5295  // Load from database
5296  $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
5297  ? wfGetDB( DB_MASTER )
5298  : wfGetDB( DB_REPLICA );
5299 
5300  $res = $dbr->select(
5301  'user_properties',
5302  [ 'up_property', 'up_value' ],
5303  [ 'up_user' => $this->getId() ],
5304  __METHOD__
5305  );
5306 
5307  $this->mOptionOverrides = [];
5308  $data = [];
5309  foreach ( $res as $row ) {
5310  // Convert '0' to 0. PHP's boolean conversion considers them both
5311  // false, but e.g. JavaScript considers the former as true.
5312  // @todo: T54542 Somehow determine the desired type (string/int/bool)
5313  // and convert all values here.
5314  if ( $row->up_value === '0' ) {
5315  $row->up_value = 0;
5316  }
5317  $data[$row->up_property] = $row->up_value;
5318  }
5319  }
5320 
5321  // Convert the email blacklist from a new line delimited string
5322  // to an array of ids.
5323  if ( isset( $data['email-blacklist'] ) && $data['email-blacklist'] ) {
5324  $data['email-blacklist'] = array_map( 'intval', explode( "\n", $data['email-blacklist'] ) );
5325  }
5326 
5327  foreach ( $data as $property => $value ) {
5328  $this->mOptionOverrides[$property] = $value;
5329  $this->mOptions[$property] = $value;
5330  }
5331  }
5332 
5333  $this->mOptionsLoaded = true;
5334 
5335  Hooks::run( 'UserLoadOptions', [ $this, &$this->mOptions ] );
5336  }
5337 
5343  protected function saveOptions() {
5344  $this->loadOptions();
5345 
5346  // Not using getOptions(), to keep hidden preferences in database
5347  $saveOptions = $this->mOptions;
5348 
5349  // Convert usernames to ids.
5350  if ( isset( $this->mOptions['email-blacklist'] ) ) {
5351  if ( $this->mOptions['email-blacklist'] ) {
5352  $value = $this->mOptions['email-blacklist'];
5353  // Email Blacklist may be an array of ids or a string of new line
5354  // delimnated user names.
5355  if ( is_array( $value ) ) {
5356  $ids = array_filter( $value, 'is_numeric' );
5357  } else {
5358  $lookup = CentralIdLookup::factory();
5359  $ids = $lookup->centralIdsFromNames( explode( "\n", $value ), $this );
5360  }
5361  $this->mOptions['email-blacklist'] = $ids;
5362  $saveOptions['email-blacklist'] = implode( "\n", $this->mOptions['email-blacklist'] );
5363  } else {
5364  // If the blacklist is empty, set it to null rather than an empty string.
5365  $this->mOptions['email-blacklist'] = null;
5366  }
5367  }
5368 
5369  // Allow hooks to abort, for instance to save to a global profile.
5370  // Reset options to default state before saving.
5371  if ( !Hooks::run( 'UserSaveOptions', [ $this, &$saveOptions ] ) ) {
5372  return;
5373  }
5374 
5375  $userId = $this->getId();
5376 
5377  $insert_rows = []; // all the new preference rows
5378  foreach ( $saveOptions as $key => $value ) {
5379  // Don't bother storing default values
5380  $defaultOption = self::getDefaultOption( $key );
5381  if ( ( $defaultOption === null && $value !== false && $value !== null )
5382  || $value != $defaultOption
5383  ) {
5384  $insert_rows[] = [
5385  'up_user' => $userId,
5386  'up_property' => $key,
5387  'up_value' => $value,
5388  ];
5389  }
5390  }
5391 
5392  $dbw = wfGetDB( DB_MASTER );
5393 
5394  $res = $dbw->select( 'user_properties',
5395  [ 'up_property', 'up_value' ], [ 'up_user' => $userId ], __METHOD__ );
5396 
5397  // Find prior rows that need to be removed or updated. These rows will
5398  // all be deleted (the latter so that INSERT IGNORE applies the new values).
5399  $keysDelete = [];
5400  foreach ( $res as $row ) {
5401  if ( !isset( $saveOptions[$row->up_property] )
5402  || strcmp( $saveOptions[$row->up_property], $row->up_value ) != 0
5403  ) {
5404  $keysDelete[] = $row->up_property;
5405  }
5406  }
5407 
5408  if ( count( $keysDelete ) ) {
5409  // Do the DELETE by PRIMARY KEY for prior rows.
5410  // In the past a very large portion of calls to this function are for setting
5411  // 'rememberpassword' for new accounts (a preference that has since been removed).
5412  // Doing a blanket per-user DELETE for new accounts with no rows in the table
5413  // caused gap locks on [max user ID,+infinity) which caused high contention since
5414  // updates would pile up on each other as they are for higher (newer) user IDs.
5415  // It might not be necessary these days, but it shouldn't hurt either.
5416  $dbw->delete( 'user_properties',
5417  [ 'up_user' => $userId, 'up_property' => $keysDelete ], __METHOD__ );
5418  }
5419  // Insert the new preference rows
5420  $dbw->insert( 'user_properties', $insert_rows, __METHOD__, [ 'IGNORE' ] );
5421  }
5422 
5429  public static function getPasswordFactory() {
5430  wfDeprecated( __METHOD__, '1.27' );
5431  $ret = new PasswordFactory();
5432  $ret->init( RequestContext::getMain()->getConfig() );
5433  return $ret;
5434  }
5435 
5460  public static function passwordChangeInputAttribs() {
5462 
5463  if ( $wgMinimalPasswordLength == 0 ) {
5464  return [];
5465  }
5466 
5467  # Note that the pattern requirement will always be satisfied if the
5468  # input is empty, so we need required in all cases.
5469 
5470  # @todo FIXME: T25769: This needs to not claim the password is required
5471  # if e-mail confirmation is being used. Since HTML5 input validation
5472  # is b0rked anyway in some browsers, just return nothing. When it's
5473  # re-enabled, fix this code to not output required for e-mail
5474  # registration.
5475  # $ret = array( 'required' );
5476  $ret = [];
5477 
5478  # We can't actually do this right now, because Opera 9.6 will print out
5479  # the entered password visibly in its error message! When other
5480  # browsers add support for this attribute, or Opera fixes its support,
5481  # we can add support with a version check to avoid doing this on Opera
5482  # versions where it will be a problem. Reported to Opera as
5483  # DSK-262266, but they don't have a public bug tracker for us to follow.
5484  /*
5485  if ( $wgMinimalPasswordLength > 1 ) {
5486  $ret['pattern'] = '.{' . intval( $wgMinimalPasswordLength ) . ',}';
5487  $ret['title'] = wfMessage( 'passwordtooshort' )
5488  ->numParams( $wgMinimalPasswordLength )->text();
5489  }
5490  */
5491 
5492  return $ret;
5493  }
5494 
5500  public static function selectFields() {
5501  return [
5502  'user_id',
5503  'user_name',
5504  'user_real_name',
5505  'user_email',
5506  'user_touched',
5507  'user_token',
5508  'user_email_authenticated',
5509  'user_email_token',
5510  'user_email_token_expires',
5511  'user_registration',
5512  'user_editcount',
5513  ];
5514  }
5515 
5523  static function newFatalPermissionDeniedStatus( $permission ) {
5524  global $wgLang;
5525 
5526  $groups = [];
5527  foreach ( self::getGroupsWithPermission( $permission ) as $group ) {
5528  $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(), 'wiki' );
5529  }
5530 
5531  if ( $groups ) {
5532  return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
5533  } else {
5534  return Status::newFatal( 'badaccess-group0' );
5535  }
5536  }
5537 
5547  public function getInstanceForUpdate() {
5548  if ( !$this->getId() ) {
5549  return null; // anon
5550  }
5551 
5552  $user = self::newFromId( $this->getId() );
5553  if ( !$user->loadFromId( self::READ_EXCLUSIVE ) ) {
5554  return null;
5555  }
5556 
5557  return $user;
5558  }
5559 
5567  public function equals( User $user ) {
5568  return $this->getName() === $user->getName();
5569  }
5570 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1619
User\saveOptions
saveOptions()
Saves the non-default options for this user, as previously set e.g.
Definition: User.php:5343
$wgHiddenPrefs
$wgHiddenPrefs
An array of preferences to not show for the user.
Definition: DefaultSettings.php:4929
Block\prevents
prevents( $action, $x=null)
Get/set whether the Block prevents a given action.
Definition: Block.php:1012
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:5460
Preferences\getPreferences
static getPreferences( $user, IContextSource $context)
Definition: Preferences.php:80
User\updateNewtalk
updateNewtalk( $field, $id, $curRev=null)
Add or update the new messages flag.
Definition: User.php:2398
User\setCookie
setCookie( $name, $value, $exp=0, $secure=null, $params=[], $request=null)
Set a cookie on the user's client.
Definition: User.php:3863
User\loadFromId
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
Definition: User.php:425
User\load
load( $flags=self::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition: User.php:362
User\getNewtalk
getNewtalk()
Check if the user has new messages.
Definition: User.php:2293
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:245
$wgProxyWhitelist
$wgProxyWhitelist
Proxy whitelist, list of addresses that are assumed to be non-proxy despite what the other methods mi...
Definition: DefaultSettings.php:5625
User\inDnsBlacklist
inDnsBlacklist( $ip, $bases)
Whether the given IP is in a given DNS blacklist.
Definition: User.php:1809
$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:244
$wgUser
$wgUser
Definition: Setup.php:809
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:573
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:268
User\confirmationTokenUrl
confirmationTokenUrl( $token)
Return a URL the user can use to confirm their email address.
Definition: User.php:4551
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:91
$wgProxyList
$wgProxyList
Big list of banned IP addresses.
Definition: DefaultSettings.php:5970
User\clearSharedCache
clearSharedCache( $mode='changed')
Clear user data from memcached.
Definition: User.php:2496
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:3364
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
User\$mToken
string $mToken
Definition: User.php:219
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:1005
User\getId
getId()
Get the user's ID.
Definition: User.php:2224
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:4971
User\useFilePatrol
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition: User.php:3599
$wgMaxArticleSize
$wgMaxArticleSize
Maximum article size in kilobytes.
Definition: DefaultSettings.php:2196
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:3510
$context
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2581
User\$mBlock
Block $mBlock
Definition: User.php:297
Block\clearCookie
static clearCookie(WebResponse $response)
Unset the 'BlockID' cookie.
Definition: Block.php:1481
User\$mBlockreason
string $mBlockreason
Definition: User.php:277
User\getTokenUrl
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
Definition: User.php:4578
User\$mImplicitGroups
array $mImplicitGroups
Definition: User.php:281
User\isLocallyBlockedProxy
static isLocallyBlockedProxy( $ip)
Check if an IP address is in the local proxy list.
Definition: User.php:1855
$wgRevokePermissions
$wgRevokePermissions
Permission keys revoked from users in each group.
Definition: DefaultSettings.php:5268
User\resetTokenFromOption
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition: User.php:3018
$wgBlockAllowsUTEdit
$wgBlockAllowsUTEdit
Set this to true to allow blocked users to edit their own user talk page.
Definition: DefaultSettings.php:5008
User\loadFromUserObject
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition: User.php:1414
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5523
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:4391
$wgShowUpdatedMarker
$wgShowUpdatedMarker
Show "Updated (since my last visit)" marker in RC view, watchlist and history view for watched pages ...
Definition: DefaultSettings.php:6925
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:668
$opt
$opt
Definition: postprocess-phan.php:115
User\isBot
isBot()
Definition: User.php:3518
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:2475
$wgExperiencedUserMemberSince
$wgExperiencedUserMemberSince
Name of the external diff engine to use.
Definition: DefaultSettings.php:8739
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:3401
User\spreadBlock
spreadBlock()
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition: User.php:4254
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:5110
User\newFromSession
static newFromSession(WebRequest $request=null)
Create a new user object using data from session.
Definition: User.php:616
captcha-old.count
count
Definition: captcha-old.py:249
wfGetLB
wfGetLB( $wiki=false)
Get a load balancer object.
Definition: GlobalFunctions.php:2869
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
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:3075
Block\chooseBlock
static chooseBlock(array $blocks, array $ipChain)
From a list of multiple blocks, find the most exact and strongest Block.
Definition: Block.php:1232
User\getBlock
getBlock( $bFromSlave=true)
Get the block affecting the user, or null if the user is not blocked.
Definition: User.php:2077
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:4690
MediaWiki\Logger\LoggerFactory\getInstance
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Definition: LoggerFactory.php:92
User\getBlockId
getBlockId()
If user is blocked, return the ID for the block.
Definition: User.php:2128
User\getIntOption
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition: User.php:2953
User\getOptions
getOptions( $flags=0)
Get all user's options.
Definition: User.php:2910
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:215
User\__construct
__construct()
Lightweight constructor for an anonymous user.
Definition: User.php:320
$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:1963
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2040
User\getToken
getToken( $forceCreation=true)
Get the user's current token.
Definition: User.php:2697
User\spreadAnyEditBlock
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition: User.php:4241
$wgSoftBlockRanges
string[] $wgSoftBlockRanges
IP ranges that should be considered soft-blocked (anon-only, account creation allowed).
Definition: DefaultSettings.php:5634
User\getNewMessageRevisionId
getNewMessageRevisionId()
Get the revision ID for the last talk page revision viewed by the talk page owner.
Definition: User.php:2356
User\loadDefaults
loadDefaults( $name=false)
Set cached properties to default.
Definition: User.php:1162
User\$mAllowUsertalk
bool $mAllowUsertalk
Definition: User.php:300
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, 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. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1245
$wgEmailAuthentication
$wgEmailAuthentication
Require email authentication before sending mail to an email address.
Definition: DefaultSettings.php:1699
User\$mOptions
array $mOptions
Definition: User.php:291
User\$mNewtalk
$mNewtalk
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition: User.php:267
User\loadOptions
loadOptions( $data=null)
Load the user options either from cache, the database or an array.
Definition: User.php:5263
User\makeGroupLinkHTML
static makeGroupLinkHTML( $group, $text='')
Create a link to the group in HTML, if available; else return the group name.
Definition: User.php:4946
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
$wgDefaultUserOptions
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
Definition: DefaultSettings.php:4862
$req
this hook is for auditing only $req
Definition: hooks.txt:988
User\setNewpassword
setNewpassword( $str, $throttle=true)
Set the password for a password reminder or new account email.
Definition: User.php:2755
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:68
User\setEmailWithConfirmation
setEmailWithConfirmation( $str)
Set the user's e-mail address and a confirmation mail if needed.
Definition: User.php:2800
$wgEnableUserEmail
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
Definition: DefaultSettings.php:1604
User\$mHideName
bool $mHideName
Definition: User.php:289
User\getStubThreshold
getStubThreshold()
Get the user preferred stub threshold.
Definition: User.php:3243
$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:1112
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1324
User\$mLocked
bool $mLocked
Definition: User.php:287
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:550
User\loadFromRow
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition: User.php:1318
User\setEmailAuthenticationTimestamp
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition: User.php:4622
IP\isIPv6
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
Definition: IP.php:88
User\$mEmail
string $mEmail
Definition: User.php:213
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:4313
User\$mOptionOverrides
array $mOptionOverrides
Definition: User.php:238
User\getGroups
getGroups()
Get the list of explicit group memberships this user has.
Definition: User.php:3299
$s
$s
Definition: mergeMessageFileList.php:188
page
target page
Definition: All_system_messages.txt:1267
User\getBlockFromCookieValue
getBlockFromCookieValue( $blockCookieVal)
Try to load a Block from an ID given in a cookie value.
Definition: User.php:1749
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:3587
$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:302
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:3204
User\isSafeToLoad
isSafeToLoad()
Test if it's safe to load this User object.
Definition: User.php:345
User\getEditTokenTimestamp
static getEditTokenTimestamp( $val)
Get the embedded timestamp from a token.
Definition: User.php:4425
User\setEmail
setEmail( $str)
Set the user's e-mail address.
Definition: User.php:2783
User\idForName
idForName( $flags=0)
If only this user's username is known, and it exists, return the user ID.
Definition: User.php:4071
$success
$success
Definition: NoLocalSettings.php:44
User\isValidUserName
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:851
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:4468
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4791
User\getEmailAuthenticationTimestamp
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition: User.php:2773
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:1926
User\$mFrom
$mFrom
String Initialization data source if mLoadedItems!==true.
Definition: User.php:262
User\initEditCount
initEditCount( $add=0)
Initialize user_editcount from data out of the revision table.
Definition: User.php:5171
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:3578
$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:4608
$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:637
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:6801
User\loadGroups
loadGroups()
Load the groups from the database if they aren't already loaded.
Definition: User.php:1424
User\$mHash
string $mHash
Definition: User.php:273
MediaWiki\Session\Token\getTimestamp
static getTimestamp( $token)
Decode the timestamp from a token string.
Definition: Token.php:61
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:6833
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:1140
User\isIPRange
isIPRange()
Is the user an IP range?
Definition: User.php:836
User\deleteNewtalk
deleteNewtalk( $field, $id)
Clear the new messages flag for the given user.
Definition: User.php:2423
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:5567
User\$mCacheVars
static $mCacheVars
Array of Strings List of member variables which are saved to the shared cache (memcached).
Definition: User.php:96
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:3258
$wgClockSkewFudge
$wgClockSkewFudge
Clock skew or the one-second resolution of time() can occasionally cause cache problems when the user...
Definition: DefaultSettings.php:2606
User\createNew
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition: User.php:4106
User\getRequest
getRequest()
Get the WebRequest object to use with this object.
Definition: User.php:3612
Preferences\getSaveBlacklist
static getSaveBlacklist()
Definition: Preferences.php:70
User\getAutomaticGroups
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:3348
User\INVALID_TOKEN
const INVALID_TOKEN
@const string An invalid value for user_token
Definition: User.php:60
User\setPassword
setPassword( $str)
Set the password and reset the random token.
Definition: User.php:2608
User\$mGroupMemberships
array $mGroupMemberships
Associative array of (group name => UserGroupMembership object)
Definition: User.php:236
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1579
User\getInstanceForUpdate
getInstanceForUpdate()
Get a new instance of this user that was loaded from the master via a locking read.
Definition: User.php:5547
$wgMaxNameChars
$wgMaxNameChars
Maximum number of bytes in username.
Definition: DefaultSettings.php:4836
User\$mGroups
array $mGroups
No longer used since 1.29; use User::getGroups() instead.
Definition: User.php:234
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:2089
$wgExperiencedUserEdits
$wgExperiencedUserEdits
Name of the external diff engine to use.
Definition: DefaultSettings.php:8738
User\newSystemUser
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition: User.php:678
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:65
User\addGroup
addGroup( $group, $expiry=null)
Add the user to the given group.
Definition: User.php:3436
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:4442
User\getEmail
getEmail()
Get the user's e-mail address.
Definition: User.php:2763
User\$mRights
array $mRights
Definition: User.php:275
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:4322
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:4175
User\makeUpdateConditions
makeUpdateConditions(Database $db, array $conditions)
Builds update conditions.
Definition: User.php:1498
IP\isValidRange
static isValidRange( $ipRange)
Validate an IP range (valid address with a valid CIDR prefix).
Definition: IP.php:138
User\invalidateCache
invalidateCache()
Immediately touch the user data cache for this account.
Definition: User.php:2520
User\setInternalPassword
setInternalPassword( $str)
Set the password and reset the random token unconditionally.
Definition: User.php:2620
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:4560
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:932
User\isLocked
isLocked()
Check if user account is locked.
Definition: User.php:2188
User\$mDatePreference
string $mDatePreference
Definition: User.php:269
$wgAuthenticationTokenVersion
string null $wgAuthenticationTokenVersion
Versioning for authentication tokens.
Definition: DefaultSettings.php:4967
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1176
$property
$property
Definition: styleTest.css.php:44
User\checkPasswordValidity
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition: User.php:1052
Skin\normalizeKey
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition: Skin.php:95
User\confirmEmail
confirmEmail()
Mark the e-mail address confirmed.
Definition: User.php:4591
User\setItemLoaded
setItemLoaded( $item)
Set that an item has been loaded.
Definition: User.php:1210
User\getPasswordFactory
static getPasswordFactory()
Lazily instantiate and return a factory object for making passwords.
Definition: User.php:5429
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:2119
User\setNewtalk
setNewtalk( $val, $curRev=null)
Update the 'You have new messages!' status.
Definition: User.php:2443
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
User\$mBlockedFromCreateAccount
Block $mBlockedFromCreateAccount
Definition: User.php:303
in
null for the wiki Added in
Definition: hooks.txt:1581
User\$mGlobalBlock
Block $mGlobalBlock
Definition: User.php:285
User\isAllowedToCreateAccount
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition: User.php:4304
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:3961
User\confirmationToken
confirmationToken(&$expiration)
Generate, store, and return a new e-mail confirmation code.
Definition: User.php:4533
User\getCacheKey
getCacheKey(WANObjectCache $cache)
Definition: User.php:466
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:2056
User\getImplicitGroups
static getImplicitGroups()
Get a list of implicit groups.
Definition: User.php:4914
User\isHidden
isHidden()
Check if user account is hidden.
Definition: User.php:2205
User\randomPassword
static randomPassword()
Return a random password.
Definition: User.php:1149
User\$mBlockedby
string $mBlockedby
Definition: User.php:271
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:742
User\isBlockedFromEmailuser
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition: User.php:4295
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:825
User\validateCache
validateCache( $timestamp)
Validate the cache for this account.
Definition: User.php:2552
User\getEffectiveGroups
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:3325
User\isPingLimitable
isPingLimitable()
Is this user subject to rate limiting?
Definition: User.php:1901
User\removeGroup
removeGroup( $group)
Remove the user from the given group.
Definition: User.php:3472
User\canReceiveEmail
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition: User.php:4650
$wgImplicitGroups
$wgImplicitGroups
Implicit groups, aren't shown on Special:Listusers or somewhere else.
Definition: DefaultSettings.php:5273
User\isNewbie
isNewbie()
Determine whether the user is a newbie.
Definition: User.php:4332
User\isAllowedAll
isAllowedAll()
Definition: User.php:3550
$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:2537
$wgExtendedLoginCookieExpiration
$wgExtendedLoginCookieExpiration
Default login cookie lifetime, in seconds.
Definition: DefaultSettings.php:5990
User\clearInstanceCache
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition: User.php:1554
$wgEnableEmail
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
Definition: DefaultSettings.php:1598
User\CHECK_USER_RIGHTS
const CHECK_USER_RIGHTS
Definition: User.php:83
$wgEnableDnsBlacklist
$wgEnableDnsBlacklist
Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open proxies.
Definition: DefaultSettings.php:5594
User\TOKEN_LENGTH
const TOKEN_LENGTH
@const int Number of characters in user_token field.
Definition: User.php:55
$wgDefaultSkin
$wgDefaultSkin
Default skin, for new users and anonymous visitors.
Definition: DefaultSettings.php:3259
$wgUpdateRowsPerQuery
$wgUpdateRowsPerQuery
Number of rows to update per query.
Definition: DefaultSettings.php:8331
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1778
$wgReservedUsernames
$wgReservedUsernames
Array of usernames which may not be registered or logged in from Maintenance scripts can still use th...
Definition: DefaultSettings.php:4842
User\clearCookie
clearCookie( $name, $secure=null, $params=[])
Clear a cookie on the user's client.
Definition: User.php:3884
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:529
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:2069
User\addWatch
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
Definition: User.php:3643
User\$mQuickTouched
string $mQuickTouched
TS_MW timestamp from cache.
Definition: User.php:217
User\setName
setName( $str)
Set the user name.
Definition: User.php:2276
DB_MASTER
const DB_MASTER
Definition: defines.php:26
User\$mRealName
string $mRealName
Definition: User.php:210
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:3159
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
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:6840
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:1047
$wgRemoveGroups
$wgRemoveGroups
Definition: DefaultSettings.php:5522
User\$mLoadedItems
$mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition: User.php:250
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:3677
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:4009
User\addNewUserLogEntry
addNewUserLogEntry( $action=false, $reason='')
Add a newuser log entry for this user.
Definition: User.php:5240
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:1151
User\$mCoreRights
static $mCoreRights
Array of Strings Core rights.
Definition: User.php:121
$wgFullyInitialised
foreach( $wgExtensionFunctions as $func) if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) if(! $wgCommandLineMode) $wgFullyInitialised
Definition: Setup.php:884
User\getFirstEditTimestamp
getFirstEditTimestamp()
Get the timestamp of the first edit.
Definition: User.php:4719
$wgAutopromoteOnceLogInRC
$wgAutopromoteOnceLogInRC
Put user rights log entries for autopromotion in recent changes?
Definition: DefaultSettings.php:5493
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2581
User\GETOPTIONS_EXCLUDE_DEFAULTS
const GETOPTIONS_EXCLUDE_DEFAULTS
Exclude user options that are set to their default value.
Definition: User.php:78
User\setRealName
setRealName( $str)
Set the user's real name.
Definition: User.php:2867
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:67
User\getNewMessageLinks
getNewMessageLinks()
Return the data needed to construct links for new talk page message alerts.
Definition: User.php:2331
User\setExtendedLoginCookie
setExtendedLoginCookie( $name, $value, $secure)
Set an extended login cookie on the user's client.
Definition: User.php:3904
$wgCookieExpiration
$wgCookieExpiration
Default cookie lifetime, in seconds.
Definition: DefaultSettings.php:5982
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:1634
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:557
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:2807
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:68
User\getFormerGroups
getFormerGroups()
Returns the groups the user has belonged to.
Definition: User.php:3377
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:745
Block\getIdFromCookieValue
static getIdFromCookieValue( $cookieValue)
Get the stored ID from the 'BlockID' cookie.
Definition: Block.php:1517
$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:5124
User\loadFromSession
loadFromSession()
Load user data from the session.
Definition: User.php:1221
$wgRateLimits
$wgRateLimits
Simple rate limiter options to brake edit floods.
Definition: DefaultSettings.php:5678
User\isBlockedGlobally
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2141
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:2882
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
User\isDnsBlacklisted
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
Definition: User.php:1788
User\getTouched
getTouched()
Get the user touched timestamp.
Definition: User.php:2564
User\$mId
int $mId
Cache variables.
Definition: User.php:206
User\getGlobalBlock
getGlobalBlock( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:2155
User\__toString
__toString()
Definition: User.php:327
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:1516
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:80
User\getRealName
getRealName()
Get the user's real name.
Definition: User.php:2855
User\$idCacheByName
static $idCacheByName
Definition: User.php:308
User\setCookies
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
Definition: User.php:3925
User\getGroupPermissions
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
Definition: User.php:4741
User\getGroupPage
static getGroupPage( $group)
Get the title of a page describing a particular group.
Definition: User.php:4931
User\changeableGroups
changeableGroups()
Returns an array of groups that this user can add and remove.
Definition: User.php:5068
User\clearAllNotifications
clearAllNotifications()
Resets all of the given user's page-change notification timestamps.
Definition: User.php:3745
User\VERSION
const VERSION
@const int Serialized record version.
Definition: User.php:72
$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:1965
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:4456
$wgAddGroups
$wgAddGroups
$wgAddGroups and $wgRemoveGroups can be used to give finer control over who can assign which groups a...
Definition: DefaultSettings.php:5517
User\checkPassword
checkPassword( $password)
Check to see if the given clear-text password is one of the accepted passwords.
Definition: User.php:4342
User\getAllGroups
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4885
User\removeWatch
removeWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Stop watching an article.
Definition: User.php:3660
User\getAllRights
static getAllRights()
Get a list of all available permissions.
Definition: User.php:4897
$wgInvalidUsernameCharacters
$wgInvalidUsernameCharacters
Characters to prevent during new account creations.
Definition: DefaultSettings.php:4936
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:470
User\blockedBy
blockedBy()
If user is blocked, return the name of the user who placed the block.
Definition: User.php:2110
$wgLearnerEdits
$wgLearnerEdits
The following variables define 3 user experience levels:
Definition: DefaultSettings.php:8736
User\getDBTouched
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition: User.php:2586
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:3081
User\getGroupMember
static getGroupMember( $group, $username='#')
Get the localized descriptive name for a member of a group, if it exists.
Definition: User.php:4874
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:99
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:4995
Block\TYPE_USER
const TYPE_USER
Definition: Block.php:83
User\getGroupName
static getGroupName( $group)
Get the localized descriptive name for a group, if it exists.
Definition: User.php:4861
$wgUseEnotif
$wgUseEnotif
Definition: Setup.php:368
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:2240
User\getExperienceLevel
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition: User.php:3816
$wgUserEmailConfirmationTokenExpiry
$wgUserEmailConfirmationTokenExpiry
The time, in seconds, when an email confirmation email expires.
Definition: DefaultSettings.php:1638
User\getRegistration
getRegistration()
Get the timestamp of account creation.
Definition: User.php:4705
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:152
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:4811
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:3535
User\getRightDescription
static getRightDescription( $right)
Get the description of a given right.
Definition: User.php:5201
User\changeAuthenticationData
changeAuthenticationData(array $data)
Changes credentials of the user.
Definition: User.php:2670
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
$wgAvailableRights
$wgAvailableRights
A list of available rights, in addition to the ones defined by the core.
Definition: DefaultSettings.php:5528
$rows
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction $rows
Definition: hooks.txt:2581
$wgRateLimitsExcludedIPs
$wgRateLimitsExcludedIPs
Array of IPs / CIDR ranges which should be excluded from rate limits.
Definition: DefaultSettings.php:5750
$options
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 & $options
Definition: hooks.txt:1965
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:370
$wgApplyIpBlocksToXff
$wgApplyIpBlocksToXff
Whether to look at the X-Forwarded-For header's list of (potentially spoofed) IPs and apply IP blocks...
Definition: DefaultSettings.php:5641
User\isLoggedIn
isLoggedIn()
Get whether the user is logged in.
Definition: User.php:3502
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:4375
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:2285
$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:781
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2735
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:765
$wgGroupPermissions
$wgGroupPermissions
Permission keys given to users in each group.
Definition: DefaultSettings.php:5129
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:805
User\$mEmailTokenExpires
string $mEmailTokenExpires
Definition: User.php:225
User\findUsersByGroup
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition: User.php:935
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:1092
User\isEmailConfirmed
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition: User.php:4664
$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:1750
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:5214
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:309
$wgDnsBlacklistUrls
$wgDnsBlacklistUrls
List of DNS blacklists to use, if $wgEnableDnsBlacklist is true.
Definition: DefaultSettings.php:5619
$keys
$keys
Definition: testCompression.php:65
NS_USER
const NS_USER
Definition: Defines.php:67
User\loadFromCache
loadFromCache()
Load user data from shared cache, given mId has already been set.
Definition: User.php:487
$wgLearnerMemberSince
$wgLearnerMemberSince
Name of the external diff engine to use.
Definition: DefaultSettings.php:8737
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1448
User\sendMail
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition: User.php:4507
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:2990
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:1965
User\$mEmailToken
string $mEmailToken
Definition: User.php:223
ManualLogEntry
Class for creating log entries manually, to inject them into the database.
Definition: LogEntry.php:400
$wgGroupsRemoveFromSelf
$wgGroupsRemoveFromSelf
Definition: DefaultSettings.php:5301
User\checkNewtalk
checkNewtalk( $field, $id)
Internal uncached check for new messages.
Definition: User.php:2383
User\loadFromDatabase
loadFromDatabase( $flags=self::READ_LATEST)
Load user and user_group data from the database.
Definition: User.php:1268
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:1110
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:5500
PasswordFactory
Factory class for creating and checking Password objects.
Definition: PasswordFactory.php:28
User\$mName
string $mName
Definition: User.php:208
User\$mRegistration
string $mRegistration
Definition: User.php:227
$wgPasswordPolicy
$wgPasswordPolicy
Password policy for local wiki users.
Definition: DefaultSettings.php:4516
$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:5252
User\newFromConfirmationCode
static newFromConfirmationCode( $code, $flags=0)
Factory method to fetch whichever user has a given email confirmation code.
Definition: User.php:592
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:88
User\$mRequest
WebRequest $mRequest
Definition: User.php:294
MediaWiki\Session\Token
Value object representing a CSRF token.
Definition: Token.php:32
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:662
User\isWatched
isWatched( $title, $checkRights=self::CHECK_USER_RIGHTS)
Check the watched status of an article.
Definition: User.php:3629
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:2941
$wgMinimalPasswordLength
$wgMinimalPasswordLength
Specifies the minimal length of a user password.
Definition: DefaultSettings.php:4735
$wgGroupsAddToSelf
$wgGroupsAddToSelf
A map of group names that the user is in, to group names that those users are allowed to add or revok...
Definition: DefaultSettings.php:5296
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:900
User\isBlocked
isBlocked( $bFromSlave=true)
Check if user is blocked.
Definition: User.php:2067
User\$mFormerGroups
array $mFormerGroups
Definition: User.php:283
CentralIdLookup\factory
static factory( $providerId=null)
Fetch a CentralIdLookup.
Definition: CentralIdLookup.php:45
$wgPasswordSender
$wgPasswordSender
Sender email address for e-mail notifications.
Definition: DefaultSettings.php:1577
User\isBlockedFromCreateAccount
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
Definition: User.php:4273
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:4415
User\requiresHTTPS
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition: User.php:3224
User\$mAllRights
static $mAllRights
String Cached results of getAllRights()
Definition: User.php:201
User\isItemLoaded
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition: User.php:1200
User\purge
static purge( $wikiId, $userId)
Definition: User.php:455
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:2969
User\$queryFlagsUsed
int $queryFlagsUsed
User::READ_* constant bitfield used to load data.
Definition: User.php:306
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:781
User\getPasswordValidity
getPasswordValidity( $password)
Given unvalidated password input, return error message on failure.
Definition: User.php:1016
User\whoIsReal
static whoIsReal( $id)
Get the real name of a user given their user ID.
Definition: User.php:755
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2249
$wgSecureLogin
$wgSecureLogin
This is to let user authenticate using https when they come from http.
Definition: DefaultSettings.php:4955
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:3973
User\getGroupMemberships
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
Definition: User.php:3312
User\canSendEmail
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition: User.php:4633
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:975
$wgNamespacesToBeSearchedDefault
$wgNamespacesToBeSearchedDefault
List of namespaces which are searched by default.
Definition: DefaultSettings.php:6487
User\getMutableCacheKeys
getMutableCacheKeys(WANObjectCache $cache)
Definition: User.php:475
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37
User\$mEffectiveGroups
array $mEffectiveGroups
Definition: User.php:279
User\setPasswordInternal
setPasswordInternal( $str)
Actually set the password and such.
Definition: User.php:2632
$wgDisableAnonTalk
$wgDisableAnonTalk
Disable links to talk pages of anonymous users (IPs) in listings on special pages like page history,...
Definition: DefaultSettings.php:6931
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:77
User\$mEditCount
int $mEditCount
Definition: User.php:229
User\$mEmailAuthenticated
string $mEmailAuthenticated
Definition: User.php:221
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:4768
User\isAllowed
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3565
MWExceptionHandler\logException
static logException( $e, $catcher=self::CAUGHT_BY_OTHER)
Log an exception to the exception log (if enabled).
Definition: MWExceptionHandler.php:613
User\listOptionKinds
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
Definition: User.php:3052
$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
$type
$type
Definition: testCompression.php:48