MediaWiki  1.23.1
User.php
Go to the documentation of this file.
1 <?php
27 define( 'USER_TOKEN_LENGTH', 32 );
28 
33 define( 'MW_USER_VERSION', 9 );
34 
39 define( 'EDIT_TOKEN_SUFFIX', '+\\' );
40 
45 class PasswordError extends MWException {
46  // NOP
47 }
48 
59 class User {
67 
72 
79  static $mCacheVars = array(
80  // user table
81  'mId',
82  'mName',
83  'mRealName',
84  'mPassword',
85  'mNewpassword',
86  'mNewpassTime',
87  'mEmail',
88  'mTouched',
89  'mToken',
90  'mEmailAuthenticated',
91  'mEmailToken',
92  'mEmailTokenExpires',
93  'mPasswordExpires',
94  'mRegistration',
95  'mEditCount',
96  // user_groups table
97  'mGroups',
98  // user_properties table
99  'mOptionOverrides',
100  );
101 
108  static $mCoreRights = array(
109  'apihighlimits',
110  'autoconfirmed',
111  'autopatrol',
112  'bigdelete',
113  'block',
114  'blockemail',
115  'bot',
116  'browsearchive',
117  'createaccount',
118  'createpage',
119  'createtalk',
120  'delete',
121  'deletedhistory',
122  'deletedtext',
123  'deletelogentry',
124  'deleterevision',
125  'edit',
126  'editinterface',
127  'editprotected',
128  'editmyoptions',
129  'editmyprivateinfo',
130  'editmyusercss',
131  'editmyuserjs',
132  'editmywatchlist',
133  'editsemiprotected',
134  'editusercssjs', #deprecated
135  'editusercss',
136  'edituserjs',
137  'hideuser',
138  'import',
139  'importupload',
140  'ipblock-exempt',
141  'markbotedits',
142  'mergehistory',
143  'minoredit',
144  'move',
145  'movefile',
146  'move-rootuserpages',
147  'move-subpages',
148  'nominornewtalk',
149  'noratelimit',
150  'override-export-depth',
151  'passwordreset',
152  'patrol',
153  'patrolmarks',
154  'protect',
155  'proxyunbannable',
156  'purge',
157  'read',
158  'reupload',
159  'reupload-own',
160  'reupload-shared',
161  'rollback',
162  'sendemail',
163  'siteadmin',
164  'suppressionlog',
165  'suppressredirect',
166  'suppressrevision',
167  'unblockself',
168  'undelete',
169  'unwatchedpages',
170  'upload',
171  'upload_by_url',
172  'userrights',
173  'userrights-interwiki',
174  'viewmyprivateinfo',
175  'viewmywatchlist',
176  'writeapi',
177  );
181  static $mAllRights = false;
182 
189 
190  protected $mPasswordExpires;
192 
198 
202  private $mLoadedItems = array();
204 
214  var $mFrom;
215 
222 
226  private $mRequest;
227 
231  var $mBlock;
232 
236  var $mAllowUsertalk;
237 
241  private $mBlockedFromCreateAccount = false;
242 
246  private $mWatchedItems = array();
247 
248  static $idCacheByName = array();
249 
260  public function __construct() {
261  $this->clearInstanceCache( 'defaults' );
262  }
263 
267  public function __toString() {
268  return $this->getName();
269  }
270 
274  public function load() {
275  if ( $this->mLoadedItems === true ) {
276  return;
277  }
278  wfProfileIn( __METHOD__ );
279 
280  // Set it now to avoid infinite recursion in accessors
281  $this->mLoadedItems = true;
282 
283  switch ( $this->mFrom ) {
284  case 'defaults':
285  $this->loadDefaults();
286  break;
287  case 'name':
288  $this->mId = self::idFromName( $this->mName );
289  if ( !$this->mId ) {
290  // Nonexistent user placeholder object
291  $this->loadDefaults( $this->mName );
292  } else {
293  $this->loadFromId();
294  }
295  break;
296  case 'id':
297  $this->loadFromId();
298  break;
299  case 'session':
300  if ( !$this->loadFromSession() ) {
301  // Loading from session failed. Load defaults.
302  $this->loadDefaults();
303  }
304  wfRunHooks( 'UserLoadAfterLoadFromSession', array( $this ) );
305  break;
306  default:
307  wfProfileOut( __METHOD__ );
308  throw new MWException( "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
309  }
310  wfProfileOut( __METHOD__ );
311  }
312 
317  public function loadFromId() {
318  global $wgMemc;
319  if ( $this->mId == 0 ) {
320  $this->loadDefaults();
321  return false;
322  }
323 
324  // Try cache
325  $key = wfMemcKey( 'user', 'id', $this->mId );
326  $data = $wgMemc->get( $key );
327  if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
328  // Object is expired, load from DB
329  $data = false;
330  }
331 
332  if ( !$data ) {
333  wfDebug( "User: cache miss for user {$this->mId}\n" );
334  // Load from DB
335  if ( !$this->loadFromDatabase() ) {
336  // Can't load from ID, user is anonymous
337  return false;
338  }
339  $this->saveToCache();
340  } else {
341  wfDebug( "User: got user {$this->mId} from cache\n" );
342  // Restore from cache
343  foreach ( self::$mCacheVars as $name ) {
344  $this->$name = $data[$name];
345  }
346  }
347 
348  $this->mLoadedItems = true;
349 
350  return true;
351  }
352 
356  public function saveToCache() {
357  $this->load();
358  $this->loadGroups();
359  $this->loadOptions();
360  if ( $this->isAnon() ) {
361  // Anonymous users are uncached
362  return;
363  }
364  $data = array();
365  foreach ( self::$mCacheVars as $name ) {
366  $data[$name] = $this->$name;
367  }
368  $data['mVersion'] = MW_USER_VERSION;
369  $key = wfMemcKey( 'user', 'id', $this->mId );
370  global $wgMemc;
371  $wgMemc->set( $key, $data );
372  }
373 
376 
393  public static function newFromName( $name, $validate = 'valid' ) {
394  if ( $validate === true ) {
395  $validate = 'valid';
396  }
397  $name = self::getCanonicalName( $name, $validate );
398  if ( $name === false ) {
399  return false;
400  } else {
401  // Create unloaded user object
402  $u = new User;
403  $u->mName = $name;
404  $u->mFrom = 'name';
405  $u->setItemLoaded( 'name' );
406  return $u;
407  }
408  }
409 
416  public static function newFromId( $id ) {
417  $u = new User;
418  $u->mId = $id;
419  $u->mFrom = 'id';
420  $u->setItemLoaded( 'id' );
421  return $u;
422  }
423 
434  public static function newFromConfirmationCode( $code ) {
435  $dbr = wfGetDB( DB_SLAVE );
436  $id = $dbr->selectField( 'user', 'user_id', array(
437  'user_email_token' => md5( $code ),
438  'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
439  ) );
440  if ( $id !== false ) {
441  return User::newFromId( $id );
442  } else {
443  return null;
444  }
445  }
446 
454  public static function newFromSession( WebRequest $request = null ) {
455  $user = new User;
456  $user->mFrom = 'session';
457  $user->mRequest = $request;
458  return $user;
459  }
460 
475  public static function newFromRow( $row, $data = null ) {
476  $user = new User;
477  $user->loadFromRow( $row, $data );
478  return $user;
479  }
480 
482 
488  public static function whoIs( $id ) {
489  return UserCache::singleton()->getProp( $id, 'name' );
490  }
491 
498  public static function whoIsReal( $id ) {
499  return UserCache::singleton()->getProp( $id, 'real_name' );
500  }
501 
507  public static function idFromName( $name ) {
509  if ( is_null( $nt ) ) {
510  // Illegal name
511  return null;
512  }
513 
514  if ( isset( self::$idCacheByName[$name] ) ) {
515  return self::$idCacheByName[$name];
516  }
517 
518  $dbr = wfGetDB( DB_SLAVE );
519  $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), __METHOD__ );
520 
521  if ( $s === false ) {
522  $result = null;
523  } else {
524  $result = $s->user_id;
525  }
526 
527  self::$idCacheByName[$name] = $result;
528 
529  if ( count( self::$idCacheByName ) > 1000 ) {
530  self::$idCacheByName = array();
531  }
532 
533  return $result;
534  }
535 
539  public static function resetIdByNameCache() {
540  self::$idCacheByName = array();
541  }
542 
559  public static function isIP( $name ) {
560  return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name ) || IP::isIPv6( $name );
561  }
562 
574  public static function isValidUserName( $name ) {
575  global $wgContLang, $wgMaxNameChars;
576 
577  if ( $name == ''
578  || User::isIP( $name )
579  || strpos( $name, '/' ) !== false
580  || strlen( $name ) > $wgMaxNameChars
581  || $name != $wgContLang->ucfirst( $name ) ) {
582  wfDebugLog( 'username', __METHOD__ .
583  ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
584  return false;
585  }
586 
587  // Ensure that the name can't be misresolved as a different title,
588  // such as with extra namespace keys at the start.
589  $parsed = Title::newFromText( $name );
590  if ( is_null( $parsed )
591  || $parsed->getNamespace()
592  || strcmp( $name, $parsed->getPrefixedText() ) ) {
593  wfDebugLog( 'username', __METHOD__ .
594  ": '$name' invalid due to ambiguous prefixes" );
595  return false;
596  }
597 
598  // Check an additional blacklist of troublemaker characters.
599  // Should these be merged into the title char list?
600  $unicodeBlacklist = '/[' .
601  '\x{0080}-\x{009f}' . # iso-8859-1 control chars
602  '\x{00a0}' . # non-breaking space
603  '\x{2000}-\x{200f}' . # various whitespace
604  '\x{2028}-\x{202f}' . # breaks and control chars
605  '\x{3000}' . # ideographic space
606  '\x{e000}-\x{f8ff}' . # private use
607  ']/u';
608  if ( preg_match( $unicodeBlacklist, $name ) ) {
609  wfDebugLog( 'username', __METHOD__ .
610  ": '$name' invalid due to blacklisted characters" );
611  return false;
612  }
613 
614  return true;
615  }
616 
628  public static function isUsableName( $name ) {
629  global $wgReservedUsernames;
630  // Must be a valid username, obviously ;)
631  if ( !self::isValidUserName( $name ) ) {
632  return false;
633  }
634 
635  static $reservedUsernames = false;
636  if ( !$reservedUsernames ) {
637  $reservedUsernames = $wgReservedUsernames;
638  wfRunHooks( 'UserGetReservedNames', array( &$reservedUsernames ) );
639  }
640 
641  // Certain names may be reserved for batch processes.
642  foreach ( $reservedUsernames as $reserved ) {
643  if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
644  $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
645  }
646  if ( $reserved == $name ) {
647  return false;
648  }
649  }
650  return true;
651  }
652 
665  public static function isCreatableName( $name ) {
666  global $wgInvalidUsernameCharacters;
667 
668  // Ensure that the username isn't longer than 235 bytes, so that
669  // (at least for the builtin skins) user javascript and css files
670  // will work. (bug 23080)
671  if ( strlen( $name ) > 235 ) {
672  wfDebugLog( 'username', __METHOD__ .
673  ": '$name' invalid due to length" );
674  return false;
675  }
676 
677  // Preg yells if you try to give it an empty string
678  if ( $wgInvalidUsernameCharacters !== '' ) {
679  if ( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) {
680  wfDebugLog( 'username', __METHOD__ .
681  ": '$name' invalid due to wgInvalidUsernameCharacters" );
682  return false;
683  }
684  }
685 
686  return self::isUsableName( $name );
687  }
688 
695  public function isValidPassword( $password ) {
696  //simple boolean wrapper for getPasswordValidity
697  return $this->getPasswordValidity( $password ) === true;
698  }
699 
700 
707  public function getPasswordValidity( $password ) {
709  if ( $result->isGood() ) {
710  return true;
711  } else {
712  $messages = array();
713  foreach ( $result->getErrorsByType( 'error' ) as $error ) {
714  $messages[] = $error['message'];
715  }
716  foreach ( $result->getErrorsByType( 'warning' ) as $warning ) {
717  $messages[] = $warning['message'];
718  }
719  if ( count( $messages ) === 1 ) {
720  return $messages[0];
721  }
722  return $messages;
723  }
724  }
725 
734  public function checkPasswordValidity( $password ) {
735  global $wgMinimalPasswordLength, $wgContLang;
736 
737  static $blockedLogins = array(
738  'Useruser' => 'Passpass', 'Useruser1' => 'Passpass1', # r75589
739  'Apitestsysop' => 'testpass', 'Apitestuser' => 'testpass' # r75605
740  );
741 
742  $status = Status::newGood();
743 
744  $result = false; //init $result to false for the internal checks
745 
746  if ( !wfRunHooks( 'isValidPassword', array( $password, &$result, $this ) ) ) {
747  $status->error( $result );
748  return $status;
749  }
750 
751  if ( $result === false ) {
752  if ( strlen( $password ) < $wgMinimalPasswordLength ) {
753  $status->error( 'passwordtooshort', $wgMinimalPasswordLength );
754  return $status;
755  } elseif ( $wgContLang->lc( $password ) == $wgContLang->lc( $this->mName ) ) {
756  $status->error( 'password-name-match' );
757  return $status;
758  } elseif ( isset( $blockedLogins[$this->getName()] ) && $password == $blockedLogins[$this->getName()] ) {
759  $status->error( 'password-login-forbidden' );
760  return $status;
761  } else {
762  //it seems weird returning a Good status here, but this is because of the
763  //initialization of $result to false above. If the hook is never run or it
764  //doesn't modify $result, then we will likely get down into this if with
765  //a valid password.
766  return $status;
767  }
768  } elseif ( $result === true ) {
769  return $status;
770  } else {
771  $status->error( $result );
772  return $status; //the isValidPassword hook set a string $result and returned true
773  }
774  }
775 
781  public function expirePassword( $ts = 0 ) {
782  $this->load();
783  $timestamp = wfTimestamp( TS_MW, $ts );
784  $this->mPasswordExpires = $timestamp;
785  $this->saveSettings();
786  }
787 
793  public function resetPasswordExpiration( $load = true ) {
794  global $wgPasswordExpirationDays;
795  if ( $load ) {
796  $this->load();
797  }
798  $newExpire = null;
799  if ( $wgPasswordExpirationDays ) {
800  $newExpire = wfTimestamp(
801  TS_MW,
802  time() + ( $wgPasswordExpirationDays * 24 * 3600 )
803  );
804  }
805  // Give extensions a chance to force an expiration
806  wfRunHooks( 'ResetPasswordExpiration', array( $this, &$newExpire ) );
807  $this->mPasswordExpires = $newExpire;
808  }
809 
819  public function getPasswordExpired() {
820  global $wgPasswordExpireGrace;
821  $expired = false;
822  $now = wfTimestamp();
823  $expiration = $this->getPasswordExpireDate();
824  $expUnix = wfTimestamp( TS_UNIX, $expiration );
825  if ( $expiration !== null && $expUnix < $now ) {
826  $expired = ( $expUnix + $wgPasswordExpireGrace < $now ) ? 'hard' : 'soft';
827  }
828  return $expired;
829  }
830 
838  public function getPasswordExpireDate() {
839  $this->load();
841  }
842 
870  public static function isValidEmailAddr( $addr ) {
871  wfDeprecated( __METHOD__, '1.18' );
872  return Sanitizer::validateEmail( $addr );
873  }
874 
888  public static function getCanonicalName( $name, $validate = 'valid' ) {
889  // Force usernames to capital
891  $name = $wgContLang->ucfirst( $name );
892 
893  # Reject names containing '#'; these will be cleaned up
894  # with title normalisation, but then it's too late to
895  # check elsewhere
896  if ( strpos( $name, '#' ) !== false ) {
897  return false;
898  }
899 
900  // Clean up name according to title rules
901  $t = ( $validate === 'valid' ) ?
903  // Check for invalid titles
904  if ( is_null( $t ) ) {
905  return false;
906  }
907 
908  // Reject various classes of invalid names
909  global $wgAuth;
910  $name = $wgAuth->getCanonicalName( $t->getText() );
911 
912  switch ( $validate ) {
913  case false:
914  break;
915  case 'valid':
916  if ( !User::isValidUserName( $name ) ) {
917  $name = false;
918  }
919  break;
920  case 'usable':
921  if ( !User::isUsableName( $name ) ) {
922  $name = false;
923  }
924  break;
925  case 'creatable':
926  if ( !User::isCreatableName( $name ) ) {
927  $name = false;
928  }
929  break;
930  default:
931  throw new MWException( 'Invalid parameter value for $validate in ' . __METHOD__ );
932  }
933  return $name;
934  }
935 
944  public static function edits( $uid ) {
945  wfDeprecated( __METHOD__, '1.21' );
946  $user = self::newFromId( $uid );
947  return $user->getEditCount();
948  }
949 
955  public static function randomPassword() {
956  global $wgMinimalPasswordLength;
957  // Decide the final password length based on our min password length, stopping at a minimum of 10 chars
958  $length = max( 10, $wgMinimalPasswordLength );
959  // Multiply by 1.25 to get the number of hex characters we need
960  $length = $length * 1.25;
961  // Generate random hex chars
962  $hex = MWCryptRand::generateHex( $length );
963  // Convert from base 16 to base 32 to get a proper password like string
964  return wfBaseConvert( $hex, 16, 32 );
965  }
966 
975  public function loadDefaults( $name = false ) {
976  wfProfileIn( __METHOD__ );
977 
978  $this->mId = 0;
979  $this->mName = $name;
980  $this->mRealName = '';
981  $this->mPassword = $this->mNewpassword = '';
982  $this->mNewpassTime = null;
983  $this->mEmail = '';
984  $this->mOptionOverrides = null;
985  $this->mOptionsLoaded = false;
986 
987  $loggedOut = $this->getRequest()->getCookie( 'LoggedOut' );
988  if ( $loggedOut !== null ) {
989  $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
990  } else {
991  $this->mTouched = '1'; # Allow any pages to be cached
992  }
993 
994  $this->mToken = null; // Don't run cryptographic functions till we need a token
995  $this->mEmailAuthenticated = null;
996  $this->mEmailToken = '';
997  $this->mEmailTokenExpires = null;
998  $this->mPasswordExpires = null;
999  $this->resetPasswordExpiration( false );
1000  $this->mRegistration = wfTimestamp( TS_MW );
1001  $this->mGroups = array();
1002 
1003  wfRunHooks( 'UserLoadDefaults', array( $this, $name ) );
1004 
1005  wfProfileOut( __METHOD__ );
1006  }
1007 
1020  public function isItemLoaded( $item, $all = 'all' ) {
1021  return ( $this->mLoadedItems === true && $all === 'all' ) ||
1022  ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1023  }
1024 
1030  protected function setItemLoaded( $item ) {
1031  if ( is_array( $this->mLoadedItems ) ) {
1032  $this->mLoadedItems[$item] = true;
1033  }
1034  }
1040  private function loadFromSession() {
1041  $result = null;
1042  wfRunHooks( 'UserLoadFromSession', array( $this, &$result ) );
1043  if ( $result !== null ) {
1044  return $result;
1045  }
1046 
1047  $request = $this->getRequest();
1048 
1049  $cookieId = $request->getCookie( 'UserID' );
1050  $sessId = $request->getSessionData( 'wsUserID' );
1051 
1052  if ( $cookieId !== null ) {
1053  $sId = intval( $cookieId );
1054  if ( $sessId !== null && $cookieId != $sessId ) {
1055  wfDebugLog( 'loginSessions', "Session user ID ($sessId) and
1056  cookie user ID ($sId) don't match!" );
1057  return false;
1058  }
1059  $request->setSessionData( 'wsUserID', $sId );
1060  } elseif ( $sessId !== null && $sessId != 0 ) {
1061  $sId = $sessId;
1062  } else {
1063  return false;
1064  }
1065 
1066  if ( $request->getSessionData( 'wsUserName' ) !== null ) {
1067  $sName = $request->getSessionData( 'wsUserName' );
1068  } elseif ( $request->getCookie( 'UserName' ) !== null ) {
1069  $sName = $request->getCookie( 'UserName' );
1070  $request->setSessionData( 'wsUserName', $sName );
1071  } else {
1072  return false;
1073  }
1074 
1075  $proposedUser = User::newFromId( $sId );
1076  if ( !$proposedUser->isLoggedIn() ) {
1077  // Not a valid ID
1078  return false;
1079  }
1080 
1081  global $wgBlockDisablesLogin;
1082  if ( $wgBlockDisablesLogin && $proposedUser->isBlocked() ) {
1083  // User blocked and we've disabled blocked user logins
1084  return false;
1085  }
1086 
1087  if ( $request->getSessionData( 'wsToken' ) ) {
1088  $passwordCorrect = ( $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' ) );
1089  $from = 'session';
1090  } elseif ( $request->getCookie( 'Token' ) ) {
1091  # Get the token from DB/cache and clean it up to remove garbage padding.
1092  # This deals with historical problems with bugs and the default column value.
1093  $token = rtrim( $proposedUser->getToken( false ) ); // correct token
1094  // Make comparison in constant time (bug 61346)
1095  $passwordCorrect = strlen( $token ) && $this->compareSecrets( $token, $request->getCookie( 'Token' ) );
1096  $from = 'cookie';
1097  } else {
1098  // No session or persistent login cookie
1099  return false;
1100  }
1101 
1102  if ( ( $sName === $proposedUser->getName() ) && $passwordCorrect ) {
1103  $this->loadFromUserObject( $proposedUser );
1104  $request->setSessionData( 'wsToken', $this->mToken );
1105  wfDebug( "User: logged in from $from\n" );
1106  return true;
1107  } else {
1108  // Invalid credentials
1109  wfDebug( "User: can't log in from $from, invalid credentials\n" );
1110  return false;
1111  }
1112  }
1113 
1120  protected function compareSecrets( $answer, $test ) {
1121  if ( strlen( $answer ) !== strlen( $test ) ) {
1122  $passwordCorrect = false;
1123  } else {
1124  $result = 0;
1125  for ( $i = 0; $i < strlen( $answer ); $i++ ) {
1126  $result |= ord( $answer[$i] ) ^ ord( $test[$i] );
1127  }
1128  $passwordCorrect = ( $result == 0 );
1129  }
1130  return $passwordCorrect;
1131  }
1132 
1139  public function loadFromDatabase() {
1140  // Paranoia
1141  $this->mId = intval( $this->mId );
1142 
1143  // Anonymous user
1144  if ( !$this->mId ) {
1145  $this->loadDefaults();
1146  return false;
1147  }
1148 
1149  $dbr = wfGetDB( DB_MASTER );
1150  $s = $dbr->selectRow( 'user', self::selectFields(), array( 'user_id' => $this->mId ), __METHOD__ );
1151 
1152  wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
1153 
1154  if ( $s !== false ) {
1155  // Initialise user table data
1156  $this->loadFromRow( $s );
1157  $this->mGroups = null; // deferred
1158  $this->getEditCount(); // revalidation for nulls
1159  return true;
1160  } else {
1161  // Invalid user_id
1162  $this->mId = 0;
1163  $this->loadDefaults();
1164  return false;
1165  }
1166  }
1167 
1177  public function loadFromRow( $row, $data = null ) {
1178  $all = true;
1179 
1180  $this->mGroups = null; // deferred
1181 
1182  if ( isset( $row->user_name ) ) {
1183  $this->mName = $row->user_name;
1184  $this->mFrom = 'name';
1185  $this->setItemLoaded( 'name' );
1186  } else {
1187  $all = false;
1188  }
1189 
1190  if ( isset( $row->user_real_name ) ) {
1191  $this->mRealName = $row->user_real_name;
1192  $this->setItemLoaded( 'realname' );
1193  } else {
1194  $all = false;
1195  }
1196 
1197  if ( isset( $row->user_id ) ) {
1198  $this->mId = intval( $row->user_id );
1199  $this->mFrom = 'id';
1200  $this->setItemLoaded( 'id' );
1201  } else {
1202  $all = false;
1203  }
1204 
1205  if ( isset( $row->user_editcount ) ) {
1206  $this->mEditCount = $row->user_editcount;
1207  } else {
1208  $all = false;
1209  }
1210 
1211  if ( isset( $row->user_password ) ) {
1212  $this->mPassword = $row->user_password;
1213  $this->mNewpassword = $row->user_newpassword;
1214  $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time );
1215  $this->mEmail = $row->user_email;
1216  $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
1217  $this->mToken = $row->user_token;
1218  if ( $this->mToken == '' ) {
1219  $this->mToken = null;
1220  }
1221  $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1222  $this->mEmailToken = $row->user_email_token;
1223  $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1224  $this->mPasswordExpires = wfTimestampOrNull( TS_MW, $row->user_password_expires );
1225  $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
1226  } else {
1227  $all = false;
1228  }
1229 
1230  if ( $all ) {
1231  $this->mLoadedItems = true;
1232  }
1233 
1234  if ( is_array( $data ) ) {
1235  if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1236  $this->mGroups = $data['user_groups'];
1237  }
1238  if ( isset( $data['user_properties'] ) && is_array( $data['user_properties'] ) ) {
1239  $this->loadOptions( $data['user_properties'] );
1240  }
1241  }
1242  }
1243 
1249  protected function loadFromUserObject( $user ) {
1250  $user->load();
1251  $user->loadGroups();
1252  $user->loadOptions();
1253  foreach ( self::$mCacheVars as $var ) {
1254  $this->$var = $user->$var;
1255  }
1256  }
1257 
1261  private function loadGroups() {
1262  if ( is_null( $this->mGroups ) ) {
1263  $dbr = wfGetDB( DB_MASTER );
1264  $res = $dbr->select( 'user_groups',
1265  array( 'ug_group' ),
1266  array( 'ug_user' => $this->mId ),
1267  __METHOD__ );
1268  $this->mGroups = array();
1269  foreach ( $res as $row ) {
1270  $this->mGroups[] = $row->ug_group;
1271  }
1272  }
1273  }
1274 
1289  public function addAutopromoteOnceGroups( $event ) {
1290  global $wgAutopromoteOnceLogInRC, $wgAuth;
1291 
1292  $toPromote = array();
1293  if ( $this->getId() ) {
1294  $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
1295  if ( count( $toPromote ) ) {
1296  $oldGroups = $this->getGroups(); // previous groups
1297 
1298  foreach ( $toPromote as $group ) {
1299  $this->addGroup( $group );
1300  }
1301  // update groups in external authentication database
1302  $wgAuth->updateExternalDBGroups( $this, $toPromote );
1303 
1304  $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
1305 
1306  $logEntry = new ManualLogEntry( 'rights', 'autopromote' );
1307  $logEntry->setPerformer( $this );
1308  $logEntry->setTarget( $this->getUserPage() );
1309  $logEntry->setParameters( array(
1310  '4::oldgroups' => $oldGroups,
1311  '5::newgroups' => $newGroups,
1312  ) );
1313  $logid = $logEntry->insert();
1314  if ( $wgAutopromoteOnceLogInRC ) {
1315  $logEntry->publish( $logid );
1316  }
1317  }
1318  }
1319  return $toPromote;
1320  }
1321 
1330  public function clearInstanceCache( $reloadFrom = false ) {
1331  $this->mNewtalk = -1;
1332  $this->mDatePreference = null;
1333  $this->mBlockedby = -1; # Unset
1334  $this->mHash = false;
1335  $this->mRights = null;
1336  $this->mEffectiveGroups = null;
1337  $this->mImplicitGroups = null;
1338  $this->mGroups = null;
1339  $this->mOptions = null;
1340  $this->mOptionsLoaded = false;
1341  $this->mEditCount = null;
1342 
1343  if ( $reloadFrom ) {
1344  $this->mLoadedItems = array();
1345  $this->mFrom = $reloadFrom;
1346  }
1347  }
1348 
1355  public static function getDefaultOptions() {
1356  global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin;
1357 
1358  static $defOpt = null;
1359  if ( !defined( 'MW_PHPUNIT_TEST' ) && $defOpt !== null ) {
1360  // Disabling this for the unit tests, as they rely on being able to change $wgContLang
1361  // mid-request and see that change reflected in the return value of this function.
1362  // Which is insane and would never happen during normal MW operation
1363  return $defOpt;
1364  }
1365 
1366  $defOpt = $wgDefaultUserOptions;
1367  // Default language setting
1368  $defOpt['language'] = $wgContLang->getCode();
1369  foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
1370  $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
1371  }
1372  foreach ( SearchEngine::searchableNamespaces() as $nsnum => $nsname ) {
1373  $defOpt['searchNs' . $nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
1374  }
1375  $defOpt['skin'] = $wgDefaultSkin;
1376 
1377  wfRunHooks( 'UserGetDefaultOptions', array( &$defOpt ) );
1378 
1379  return $defOpt;
1380  }
1381 
1388  public static function getDefaultOption( $opt ) {
1389  $defOpts = self::getDefaultOptions();
1390  if ( isset( $defOpts[$opt] ) ) {
1391  return $defOpts[$opt];
1392  } else {
1393  return null;
1394  }
1395  }
1396 
1404  private function getBlockedStatus( $bFromSlave = true ) {
1405  global $wgProxyWhitelist, $wgUser, $wgApplyIpBlocksToXff;
1406 
1407  if ( -1 != $this->mBlockedby ) {
1408  return;
1409  }
1410 
1411  wfProfileIn( __METHOD__ );
1412  wfDebug( __METHOD__ . ": checking...\n" );
1413 
1414  // Initialize data...
1415  // Otherwise something ends up stomping on $this->mBlockedby when
1416  // things get lazy-loaded later, causing false positive block hits
1417  // due to -1 !== 0. Probably session-related... Nothing should be
1418  // overwriting mBlockedby, surely?
1419  $this->load();
1420 
1421  # We only need to worry about passing the IP address to the Block generator if the
1422  # user is not immune to autoblocks/hardblocks, and they are the current user so we
1423  # know which IP address they're actually coming from
1424  if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->getID() == $wgUser->getID() ) {
1425  $ip = $this->getRequest()->getIP();
1426  } else {
1427  $ip = null;
1428  }
1429 
1430  // User/IP blocking
1431  $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
1432 
1433  // Proxy blocking
1434  if ( !$block instanceof Block && $ip !== null && !$this->isAllowed( 'proxyunbannable' )
1435  && !in_array( $ip, $wgProxyWhitelist )
1436  ) {
1437  // Local list
1438  if ( self::isLocallyBlockedProxy( $ip ) ) {
1439  $block = new Block;
1440  $block->setBlocker( wfMessage( 'proxyblocker' )->text() );
1441  $block->mReason = wfMessage( 'proxyblockreason' )->text();
1442  $block->setTarget( $ip );
1443  } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
1444  $block = new Block;
1445  $block->setBlocker( wfMessage( 'sorbs' )->text() );
1446  $block->mReason = wfMessage( 'sorbsreason' )->text();
1447  $block->setTarget( $ip );
1448  }
1449  }
1450 
1451  // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
1452  if ( !$block instanceof Block
1453  && $wgApplyIpBlocksToXff
1454  && $ip !== null
1455  && !$this->isAllowed( 'proxyunbannable' )
1456  && !in_array( $ip, $wgProxyWhitelist )
1457  ) {
1458  $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
1459  $xff = array_map( 'trim', explode( ',', $xff ) );
1460  $xff = array_diff( $xff, array( $ip ) );
1461  $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromSlave );
1462  $block = Block::chooseBlock( $xffblocks, $xff );
1463  if ( $block instanceof Block ) {
1464  # Mangle the reason to alert the user that the block
1465  # originated from matching the X-Forwarded-For header.
1466  $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->text();
1467  }
1468  }
1469 
1470  if ( $block instanceof Block ) {
1471  wfDebug( __METHOD__ . ": Found block.\n" );
1472  $this->mBlock = $block;
1473  $this->mBlockedby = $block->getByName();
1474  $this->mBlockreason = $block->mReason;
1475  $this->mHideName = $block->mHideName;
1476  $this->mAllowUsertalk = !$block->prevents( 'editownusertalk' );
1477  } else {
1478  $this->mBlockedby = '';
1479  $this->mHideName = 0;
1480  $this->mAllowUsertalk = false;
1481  }
1482 
1483  // Extensions
1484  wfRunHooks( 'GetBlockedStatus', array( &$this ) );
1485 
1486  wfProfileOut( __METHOD__ );
1487  }
1488 
1496  public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
1497  global $wgEnableSorbs, $wgEnableDnsBlacklist,
1498  $wgSorbsUrl, $wgDnsBlacklistUrls, $wgProxyWhitelist;
1499 
1500  if ( !$wgEnableDnsBlacklist && !$wgEnableSorbs ) {
1501  return false;
1502  }
1503 
1504  if ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) ) {
1505  return false;
1506  }
1507 
1508  $urls = array_merge( $wgDnsBlacklistUrls, (array)$wgSorbsUrl );
1509  return $this->inDnsBlacklist( $ip, $urls );
1510  }
1511 
1519  public function inDnsBlacklist( $ip, $bases ) {
1520  wfProfileIn( __METHOD__ );
1521 
1522  $found = false;
1523  // @todo FIXME: IPv6 ??? (http://bugs.php.net/bug.php?id=33170)
1524  if ( IP::isIPv4( $ip ) ) {
1525  // Reverse IP, bug 21255
1526  $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
1527 
1528  foreach ( (array)$bases as $base ) {
1529  // Make hostname
1530  // If we have an access key, use that too (ProjectHoneypot, etc.)
1531  if ( is_array( $base ) ) {
1532  if ( count( $base ) >= 2 ) {
1533  // Access key is 1, base URL is 0
1534  $host = "{$base[1]}.$ipReversed.{$base[0]}";
1535  } else {
1536  $host = "$ipReversed.{$base[0]}";
1537  }
1538  } else {
1539  $host = "$ipReversed.$base";
1540  }
1541 
1542  // Send query
1543  $ipList = gethostbynamel( $host );
1544 
1545  if ( $ipList ) {
1546  wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!" );
1547  $found = true;
1548  break;
1549  } else {
1550  wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base." );
1551  }
1552  }
1553  }
1554 
1555  wfProfileOut( __METHOD__ );
1556  return $found;
1557  }
1558 
1566  public static function isLocallyBlockedProxy( $ip ) {
1567  global $wgProxyList;
1568 
1569  if ( !$wgProxyList ) {
1570  return false;
1571  }
1572  wfProfileIn( __METHOD__ );
1573 
1574  if ( !is_array( $wgProxyList ) ) {
1575  // Load from the specified file
1576  $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
1577  }
1578 
1579  if ( !is_array( $wgProxyList ) ) {
1580  $ret = false;
1581  } elseif ( array_search( $ip, $wgProxyList ) !== false ) {
1582  $ret = true;
1583  } elseif ( array_key_exists( $ip, $wgProxyList ) ) {
1584  // Old-style flipped proxy list
1585  $ret = true;
1586  } else {
1587  $ret = false;
1588  }
1589  wfProfileOut( __METHOD__ );
1590  return $ret;
1591  }
1592 
1598  public function isPingLimitable() {
1599  global $wgRateLimitsExcludedIPs;
1600  if ( in_array( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
1601  // No other good way currently to disable rate limits
1602  // for specific IPs. :P
1603  // But this is a crappy hack and should die.
1604  return false;
1605  }
1606  return !$this->isAllowed( 'noratelimit' );
1607  }
1608 
1620  public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1621  // Call the 'PingLimiter' hook
1622  $result = false;
1623  if ( !wfRunHooks( 'PingLimiter', array( &$this, $action, &$result, $incrBy ) ) ) {
1624  return $result;
1625  }
1626 
1627  global $wgRateLimits;
1628  if ( !isset( $wgRateLimits[$action] ) ) {
1629  return false;
1630  }
1631 
1632  // Some groups shouldn't trigger the ping limiter, ever
1633  if ( !$this->isPingLimitable() ) {
1634  return false;
1635  }
1636 
1637  global $wgMemc;
1638  wfProfileIn( __METHOD__ );
1639 
1640  $limits = $wgRateLimits[$action];
1641  $keys = array();
1642  $id = $this->getId();
1643  $userLimit = false;
1644 
1645  if ( isset( $limits['anon'] ) && $id == 0 ) {
1646  $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
1647  }
1648 
1649  if ( isset( $limits['user'] ) && $id != 0 ) {
1650  $userLimit = $limits['user'];
1651  }
1652  if ( $this->isNewbie() ) {
1653  if ( isset( $limits['newbie'] ) && $id != 0 ) {
1654  $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
1655  }
1656  if ( isset( $limits['ip'] ) ) {
1657  $ip = $this->getRequest()->getIP();
1658  $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
1659  }
1660  if ( isset( $limits['subnet'] ) ) {
1661  $ip = $this->getRequest()->getIP();
1662  $matches = array();
1663  $subnet = false;
1664  if ( IP::isIPv6( $ip ) ) {
1665  $parts = IP::parseRange( "$ip/64" );
1666  $subnet = $parts[0];
1667  } elseif ( preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
1668  // IPv4
1669  $subnet = $matches[1];
1670  }
1671  if ( $subnet !== false ) {
1672  $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
1673  }
1674  }
1675  }
1676  // Check for group-specific permissions
1677  // If more than one group applies, use the group with the highest limit
1678  foreach ( $this->getGroups() as $group ) {
1679  if ( isset( $limits[$group] ) ) {
1680  if ( $userLimit === false || $limits[$group] > $userLimit ) {
1681  $userLimit = $limits[$group];
1682  }
1683  }
1684  }
1685  // Set the user limit key
1686  if ( $userLimit !== false ) {
1687  list( $max, $period ) = $userLimit;
1688  wfDebug( __METHOD__ . ": effective user limit: $max in {$period}s\n" );
1689  $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $userLimit;
1690  }
1691 
1692  $triggered = false;
1693  foreach ( $keys as $key => $limit ) {
1694  list( $max, $period ) = $limit;
1695  $summary = "(limit $max in {$period}s)";
1696  $count = $wgMemc->get( $key );
1697  // Already pinged?
1698  if ( $count ) {
1699  if ( $count >= $max ) {
1700  wfDebugLog( 'ratelimit', $this->getName() . " tripped! $key at $count $summary" );
1701  $triggered = true;
1702  } else {
1703  wfDebug( __METHOD__ . ": ok. $key at $count $summary\n" );
1704  }
1705  } else {
1706  wfDebug( __METHOD__ . ": adding record for $key $summary\n" );
1707  if ( $incrBy > 0 ) {
1708  $wgMemc->add( $key, 0, intval( $period ) ); // first ping
1709  }
1710  }
1711  if ( $incrBy > 0 ) {
1712  $wgMemc->incr( $key, $incrBy );
1713  }
1714  }
1715 
1716  wfProfileOut( __METHOD__ );
1717  return $triggered;
1718  }
1719 
1726  public function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
1727  return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
1728  }
1729 
1736  public function getBlock( $bFromSlave = true ) {
1737  $this->getBlockedStatus( $bFromSlave );
1738  return $this->mBlock instanceof Block ? $this->mBlock : null;
1739  }
1740 
1748  public function isBlockedFrom( $title, $bFromSlave = false ) {
1749  global $wgBlockAllowsUTEdit;
1750  wfProfileIn( __METHOD__ );
1751 
1752  $blocked = $this->isBlocked( $bFromSlave );
1753  $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
1754  // If a user's name is suppressed, they cannot make edits anywhere
1755  if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName()
1756  && $title->getNamespace() == NS_USER_TALK ) {
1757  $blocked = false;
1758  wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
1759  }
1760 
1761  wfRunHooks( 'UserIsBlockedFrom', array( $this, $title, &$blocked, &$allowUsertalk ) );
1762 
1763  wfProfileOut( __METHOD__ );
1764  return $blocked;
1765  }
1771  public function blockedBy() {
1772  $this->getBlockedStatus();
1773  return $this->mBlockedby;
1774  }
1780  public function blockedFor() {
1781  $this->getBlockedStatus();
1782  return $this->mBlockreason;
1783  }
1789  public function getBlockId() {
1790  $this->getBlockedStatus();
1791  return ( $this->mBlock ? $this->mBlock->getId() : false );
1792  }
1793 
1802  public function isBlockedGlobally( $ip = '' ) {
1803  if ( $this->mBlockedGlobally !== null ) {
1804  return $this->mBlockedGlobally;
1805  }
1806  // User is already an IP?
1807  if ( IP::isIPAddress( $this->getName() ) ) {
1808  $ip = $this->getName();
1809  } elseif ( !$ip ) {
1810  $ip = $this->getRequest()->getIP();
1811  }
1812  $blocked = false;
1813  wfRunHooks( 'UserIsBlockedGlobally', array( &$this, $ip, &$blocked ) );
1814  $this->mBlockedGlobally = (bool)$blocked;
1815  return $this->mBlockedGlobally;
1816  }
1817 
1823  public function isLocked() {
1824  if ( $this->mLocked !== null ) {
1825  return $this->mLocked;
1826  }
1827  global $wgAuth;
1828  StubObject::unstub( $wgAuth );
1829  $authUser = $wgAuth->getUserInstance( $this );
1830  $this->mLocked = (bool)$authUser->isLocked();
1831  return $this->mLocked;
1832  }
1833 
1839  public function isHidden() {
1840  if ( $this->mHideName !== null ) {
1841  return $this->mHideName;
1842  }
1843  $this->getBlockedStatus();
1844  if ( !$this->mHideName ) {
1845  global $wgAuth;
1846  StubObject::unstub( $wgAuth );
1847  $authUser = $wgAuth->getUserInstance( $this );
1848  $this->mHideName = (bool)$authUser->isHidden();
1849  }
1850  return $this->mHideName;
1851  }
1857  public function getId() {
1858  if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
1859  // Special case, we know the user is anonymous
1860  return 0;
1861  } elseif ( !$this->isItemLoaded( 'id' ) ) {
1862  // Don't load if this was initialized from an ID
1863  $this->load();
1864  }
1865  return $this->mId;
1866  }
1872  public function setId( $v ) {
1873  $this->mId = $v;
1874  $this->clearInstanceCache( 'id' );
1875  }
1881  public function getName() {
1882  if ( $this->isItemLoaded( 'name', 'only' ) ) {
1883  // Special case optimisation
1884  return $this->mName;
1885  } else {
1886  $this->load();
1887  if ( $this->mName === false ) {
1888  // Clean up IPs
1889  $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
1890  }
1891  return $this->mName;
1892  }
1893  }
1894 
1908  public function setName( $str ) {
1909  $this->load();
1910  $this->mName = $str;
1911  }
1917  public function getTitleKey() {
1918  return str_replace( ' ', '_', $this->getName() );
1919  }
1925  public function getNewtalk() {
1926  $this->load();
1927 
1928  // Load the newtalk status if it is unloaded (mNewtalk=-1)
1929  if ( $this->mNewtalk === -1 ) {
1930  $this->mNewtalk = false; # reset talk page status
1931 
1932  // Check memcached separately for anons, who have no
1933  // entire User object stored in there.
1934  if ( !$this->mId ) {
1935  global $wgDisableAnonTalk;
1936  if ( $wgDisableAnonTalk ) {
1937  // Anon newtalk disabled by configuration.
1938  $this->mNewtalk = false;
1939  } else {
1940  global $wgMemc;
1941  $key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
1942  $newtalk = $wgMemc->get( $key );
1943  if ( strval( $newtalk ) !== '' ) {
1944  $this->mNewtalk = (bool)$newtalk;
1945  } else {
1946  // Since we are caching this, make sure it is up to date by getting it
1947  // from the master
1948  $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName(), true );
1949  $wgMemc->set( $key, (int)$this->mNewtalk, 1800 );
1950  }
1951  }
1952  } else {
1953  $this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
1954  }
1955  }
1956 
1957  return (bool)$this->mNewtalk;
1958  }
1959 
1973  public function getNewMessageLinks() {
1974  $talks = array();
1975  if ( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) ) {
1976  return $talks;
1977  } elseif ( !$this->getNewtalk() ) {
1978  return array();
1979  }
1980  $utp = $this->getTalkPage();
1981  $dbr = wfGetDB( DB_SLAVE );
1982  // Get the "last viewed rev" timestamp from the oldest message notification
1983  $timestamp = $dbr->selectField( 'user_newtalk',
1984  'MIN(user_last_timestamp)',
1985  $this->isAnon() ? array( 'user_ip' => $this->getName() ) : array( 'user_id' => $this->getID() ),
1986  __METHOD__ );
1988  return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ) );
1989  }
1990 
1996  public function getNewMessageRevisionId() {
1997  $newMessageRevisionId = null;
1998  $newMessageLinks = $this->getNewMessageLinks();
1999  if ( $newMessageLinks ) {
2000  // Note: getNewMessageLinks() never returns more than a single link
2001  // and it is always for the same wiki, but we double-check here in
2002  // case that changes some time in the future.
2003  if ( count( $newMessageLinks ) === 1
2004  && $newMessageLinks[0]['wiki'] === wfWikiID()
2005  && $newMessageLinks[0]['rev']
2006  ) {
2007  $newMessageRevision = $newMessageLinks[0]['rev'];
2008  $newMessageRevisionId = $newMessageRevision->getId();
2009  }
2010  }
2011  return $newMessageRevisionId;
2012  }
2013 
2023  protected function checkNewtalk( $field, $id, $fromMaster = false ) {
2024  if ( $fromMaster ) {
2025  $db = wfGetDB( DB_MASTER );
2026  } else {
2027  $db = wfGetDB( DB_SLAVE );
2028  }
2029  $ok = $db->selectField( 'user_newtalk', $field,
2030  array( $field => $id ), __METHOD__ );
2031  return $ok !== false;
2032  }
2033 
2041  protected function updateNewtalk( $field, $id, $curRev = null ) {
2042  // Get timestamp of the talk page revision prior to the current one
2043  $prevRev = $curRev ? $curRev->getPrevious() : false;
2044  $ts = $prevRev ? $prevRev->getTimestamp() : null;
2045  // Mark the user as having new messages since this revision
2046  $dbw = wfGetDB( DB_MASTER );
2047  $dbw->insert( 'user_newtalk',
2048  array( $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ),
2049  __METHOD__,
2050  'IGNORE' );
2051  if ( $dbw->affectedRows() ) {
2052  wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
2053  return true;
2054  } else {
2055  wfDebug( __METHOD__ . " already set ($field, $id)\n" );
2056  return false;
2057  }
2058  }
2059 
2066  protected function deleteNewtalk( $field, $id ) {
2067  $dbw = wfGetDB( DB_MASTER );
2068  $dbw->delete( 'user_newtalk',
2069  array( $field => $id ),
2070  __METHOD__ );
2071  if ( $dbw->affectedRows() ) {
2072  wfDebug( __METHOD__ . ": killed on ($field, $id)\n" );
2073  return true;
2074  } else {
2075  wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
2076  return false;
2077  }
2078  }
2079 
2085  public function setNewtalk( $val, $curRev = null ) {
2086  if ( wfReadOnly() ) {
2087  return;
2088  }
2089 
2090  $this->load();
2091  $this->mNewtalk = $val;
2092 
2093  if ( $this->isAnon() ) {
2094  $field = 'user_ip';
2095  $id = $this->getName();
2096  } else {
2097  $field = 'user_id';
2098  $id = $this->getId();
2099  }
2100  global $wgMemc;
2101 
2102  if ( $val ) {
2103  $changed = $this->updateNewtalk( $field, $id, $curRev );
2104  } else {
2105  $changed = $this->deleteNewtalk( $field, $id );
2106  }
2107 
2108  if ( $this->isAnon() ) {
2109  // Anons have a separate memcached space, since
2110  // user records aren't kept for them.
2111  $key = wfMemcKey( 'newtalk', 'ip', $id );
2112  $wgMemc->set( $key, $val ? 1 : 0, 1800 );
2113  }
2114  if ( $changed ) {
2115  $this->invalidateCache();
2116  }
2117  }
2118 
2124  private static function newTouchedTimestamp() {
2125  global $wgClockSkewFudge;
2126  return wfTimestamp( TS_MW, time() + $wgClockSkewFudge );
2127  }
2128 
2136  private function clearSharedCache() {
2137  $this->load();
2138  if ( $this->mId ) {
2139  global $wgMemc;
2140  $wgMemc->delete( wfMemcKey( 'user', 'id', $this->mId ) );
2141  }
2142  }
2143 
2149  public function invalidateCache() {
2150  if ( wfReadOnly() ) {
2151  return;
2152  }
2153  $this->load();
2154  if ( $this->mId ) {
2155  $this->mTouched = self::newTouchedTimestamp();
2156 
2157  $dbw = wfGetDB( DB_MASTER );
2158  $userid = $this->mId;
2159  $touched = $this->mTouched;
2160  $method = __METHOD__;
2161  $dbw->onTransactionIdle( function() use ( $dbw, $userid, $touched, $method ) {
2162  // Prevent contention slams by checking user_touched first
2163  $encTouched = $dbw->addQuotes( $dbw->timestamp( $touched ) );
2164  $needsPurge = $dbw->selectField( 'user', '1',
2165  array( 'user_id' => $userid, 'user_touched < ' . $encTouched ) );
2166  if ( $needsPurge ) {
2167  $dbw->update( 'user',
2168  array( 'user_touched' => $dbw->timestamp( $touched ) ),
2169  array( 'user_id' => $userid, 'user_touched < ' . $encTouched ),
2170  $method
2171  );
2172  }
2173  } );
2174  $this->clearSharedCache();
2175  }
2176  }
2177 
2183  public function validateCache( $timestamp ) {
2184  $this->load();
2185  return ( $timestamp >= $this->mTouched );
2186  }
2192  public function getTouched() {
2193  $this->load();
2194  return $this->mTouched;
2195  }
2196 
2213  public function setPassword( $str ) {
2214  global $wgAuth;
2215 
2216  if ( $str !== null ) {
2217  if ( !$wgAuth->allowPasswordChange() ) {
2218  throw new PasswordError( wfMessage( 'password-change-forbidden' )->text() );
2219  }
2220 
2221  if ( !$this->isValidPassword( $str ) ) {
2222  global $wgMinimalPasswordLength;
2223  $valid = $this->getPasswordValidity( $str );
2224  if ( is_array( $valid ) ) {
2225  $message = array_shift( $valid );
2226  $params = $valid;
2227  } else {
2228  $message = $valid;
2229  $params = array( $wgMinimalPasswordLength );
2230  }
2231  throw new PasswordError( wfMessage( $message, $params )->text() );
2232  }
2233  }
2234 
2235  if ( !$wgAuth->setPassword( $this, $str ) ) {
2236  throw new PasswordError( wfMessage( 'externaldberror' )->text() );
2237  }
2238 
2239  $this->setInternalPassword( $str );
2240 
2241  return true;
2242  }
2243 
2251  public function setInternalPassword( $str ) {
2252  $this->load();
2253  $this->setToken();
2254 
2255  if ( $str === null ) {
2256  // Save an invalid hash...
2257  $this->mPassword = '';
2258  } else {
2259  $this->mPassword = self::crypt( $str );
2260  }
2261  $this->mNewpassword = '';
2262  $this->mNewpassTime = null;
2263  }
2264 
2270  public function getToken( $forceCreation = true ) {
2271  $this->load();
2272  if ( !$this->mToken && $forceCreation ) {
2273  $this->setToken();
2274  }
2275  return $this->mToken;
2276  }
2277 
2284  public function setToken( $token = false ) {
2285  $this->load();
2286  if ( !$token ) {
2287  $this->mToken = MWCryptRand::generateHex( USER_TOKEN_LENGTH );
2288  } else {
2289  $this->mToken = $token;
2290  }
2291  }
2292 
2300  public function setNewpassword( $str, $throttle = true ) {
2301  $this->load();
2302 
2303  if ( $str === null ) {
2304  $this->mNewpassword = '';
2305  $this->mNewpassTime = null;
2306  } else {
2307  $this->mNewpassword = self::crypt( $str );
2308  if ( $throttle ) {
2309  $this->mNewpassTime = wfTimestampNow();
2310  }
2311  }
2312  }
2313 
2319  public function isPasswordReminderThrottled() {
2320  global $wgPasswordReminderResendTime;
2321  $this->load();
2322  if ( !$this->mNewpassTime || !$wgPasswordReminderResendTime ) {
2323  return false;
2324  }
2325  $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgPasswordReminderResendTime * 3600;
2326  return time() < $expiry;
2327  }
2333  public function getEmail() {
2334  $this->load();
2335  wfRunHooks( 'UserGetEmail', array( $this, &$this->mEmail ) );
2336  return $this->mEmail;
2337  }
2343  public function getEmailAuthenticationTimestamp() {
2344  $this->load();
2345  wfRunHooks( 'UserGetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
2347  }
2353  public function setEmail( $str ) {
2354  $this->load();
2355  if ( $str == $this->mEmail ) {
2356  return;
2357  }
2358  $this->mEmail = $str;
2359  $this->invalidateEmail();
2360  wfRunHooks( 'UserSetEmail', array( $this, &$this->mEmail ) );
2361  }
2362 
2370  public function setEmailWithConfirmation( $str ) {
2371  global $wgEnableEmail, $wgEmailAuthentication;
2372 
2373  if ( !$wgEnableEmail ) {
2374  return Status::newFatal( 'emaildisabled' );
2375  }
2376 
2377  $oldaddr = $this->getEmail();
2378  if ( $str === $oldaddr ) {
2379  return Status::newGood( true );
2380  }
2381 
2382  $this->setEmail( $str );
2383 
2384  if ( $str !== '' && $wgEmailAuthentication ) {
2385  // Send a confirmation request to the new address if needed
2386  $type = $oldaddr != '' ? 'changed' : 'set';
2387  $result = $this->sendConfirmationMail( $type );
2388  if ( $result->isGood() ) {
2389  // Say the the caller that a confirmation mail has been sent
2390  $result->value = 'eauth';
2391  }
2392  } else {
2393  $result = Status::newGood( true );
2394  }
2395 
2396  return $result;
2397  }
2403  public function getRealName() {
2404  if ( !$this->isItemLoaded( 'realname' ) ) {
2405  $this->load();
2406  }
2407 
2408  return $this->mRealName;
2409  }
2415  public function setRealName( $str ) {
2416  $this->load();
2417  $this->mRealName = $str;
2418  }
2419 
2430  public function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
2431  global $wgHiddenPrefs;
2432  $this->loadOptions();
2433 
2434  # We want 'disabled' preferences to always behave as the default value for
2435  # users, even if they have set the option explicitly in their settings (ie they
2436  # set it, and then it was disabled removing their ability to change it). But
2437  # we don't want to erase the preferences in the database in case the preference
2438  # is re-enabled again. So don't touch $mOptions, just override the returned value
2439  if ( !$ignoreHidden && in_array( $oname, $wgHiddenPrefs ) ) {
2440  return self::getDefaultOption( $oname );
2441  }
2442 
2443  if ( array_key_exists( $oname, $this->mOptions ) ) {
2444  return $this->mOptions[$oname];
2445  } else {
2446  return $defaultOverride;
2447  }
2448  }
2449 
2455  public function getOptions() {
2456  global $wgHiddenPrefs;
2457  $this->loadOptions();
2459 
2460  # We want 'disabled' preferences to always behave as the default value for
2461  # users, even if they have set the option explicitly in their settings (ie they
2462  # set it, and then it was disabled removing their ability to change it). But
2463  # we don't want to erase the preferences in the database in case the preference
2464  # is re-enabled again. So don't touch $mOptions, just override the returned value
2465  foreach ( $wgHiddenPrefs as $pref ) {
2466  $default = self::getDefaultOption( $pref );
2467  if ( $default !== null ) {
2468  $options[$pref] = $default;
2469  }
2470  }
2471 
2472  return $options;
2473  }
2474 
2482  public function getBoolOption( $oname ) {
2483  return (bool)$this->getOption( $oname );
2484  }
2485 
2494  public function getIntOption( $oname, $defaultOverride = 0 ) {
2495  $val = $this->getOption( $oname );
2496  if ( $val == '' ) {
2497  $val = $defaultOverride;
2498  }
2499  return intval( $val );
2500  }
2501 
2508  public function setOption( $oname, $val ) {
2509  $this->loadOptions();
2510 
2511  // Explicitly NULL values should refer to defaults
2512  if ( is_null( $val ) ) {
2513  $val = self::getDefaultOption( $oname );
2514  }
2515 
2516  $this->mOptions[$oname] = $val;
2517  }
2518 
2528  public function getTokenFromOption( $oname ) {
2529  global $wgHiddenPrefs;
2530  if ( in_array( $oname, $wgHiddenPrefs ) ) {
2531  return false;
2532  }
2533 
2534  $token = $this->getOption( $oname );
2535  if ( !$token ) {
2536  $token = $this->resetTokenFromOption( $oname );
2537  $this->saveSettings();
2538  }
2539  return $token;
2540  }
2541 
2551  public function resetTokenFromOption( $oname ) {
2552  global $wgHiddenPrefs;
2553  if ( in_array( $oname, $wgHiddenPrefs ) ) {
2554  return false;
2555  }
2556 
2557  $token = MWCryptRand::generateHex( 40 );
2558  $this->setOption( $oname, $token );
2559  return $token;
2560  }
2561 
2585  public static function listOptionKinds() {
2586  return array(
2587  'registered',
2588  'registered-multiselect',
2589  'registered-checkmatrix',
2590  'userjs',
2591  'special',
2592  'unused'
2593  );
2594  }
2595 
2607  public function getOptionKinds( IContextSource $context, $options = null ) {
2608  $this->loadOptions();
2609  if ( $options === null ) {
2611  }
2612 
2613  $prefs = Preferences::getPreferences( $this, $context );
2614  $mapping = array();
2615 
2616  // Pull out the "special" options, so they don't get converted as
2617  // multiselect or checkmatrix.
2618  $specialOptions = array_fill_keys( Preferences::getSaveBlacklist(), true );
2619  foreach ( $specialOptions as $name => $value ) {
2620  unset( $prefs[$name] );
2621  }
2622 
2623  // Multiselect and checkmatrix options are stored in the database with
2624  // one key per option, each having a boolean value. Extract those keys.
2625  $multiselectOptions = array();
2626  foreach ( $prefs as $name => $info ) {
2627  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
2628  ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
2629  $opts = HTMLFormField::flattenOptions( $info['options'] );
2630  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
2631 
2632  foreach ( $opts as $value ) {
2633  $multiselectOptions["$prefix$value"] = true;
2634  }
2635 
2636  unset( $prefs[$name] );
2637  }
2638  }
2639  $checkmatrixOptions = array();
2640  foreach ( $prefs as $name => $info ) {
2641  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
2642  ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
2643  $columns = HTMLFormField::flattenOptions( $info['columns'] );
2644  $rows = HTMLFormField::flattenOptions( $info['rows'] );
2645  $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
2646 
2647  foreach ( $columns as $column ) {
2648  foreach ( $rows as $row ) {
2649  $checkmatrixOptions["$prefix-$column-$row"] = true;
2650  }
2651  }
2652 
2653  unset( $prefs[$name] );
2654  }
2655  }
2656 
2657  // $value is ignored
2658  foreach ( $options as $key => $value ) {
2659  if ( isset( $prefs[$key] ) ) {
2660  $mapping[$key] = 'registered';
2661  } elseif ( isset( $multiselectOptions[$key] ) ) {
2662  $mapping[$key] = 'registered-multiselect';
2663  } elseif ( isset( $checkmatrixOptions[$key] ) ) {
2664  $mapping[$key] = 'registered-checkmatrix';
2665  } elseif ( isset( $specialOptions[$key] ) ) {
2666  $mapping[$key] = 'special';
2667  } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
2668  $mapping[$key] = 'userjs';
2669  } else {
2670  $mapping[$key] = 'unused';
2671  }
2672  }
2673 
2674  return $mapping;
2675  }
2676 
2691  public function resetOptions(
2692  $resetKinds = array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ),
2693  IContextSource $context = null
2694  ) {
2695  $this->load();
2697 
2698  if ( !is_array( $resetKinds ) ) {
2699  $resetKinds = array( $resetKinds );
2700  }
2701 
2702  if ( in_array( 'all', $resetKinds ) ) {
2703  $newOptions = $defaultOptions;
2704  } else {
2705  if ( $context === null ) {
2706  $context = RequestContext::getMain();
2707  }
2708 
2709  $optionKinds = $this->getOptionKinds( $context );
2710  $resetKinds = array_intersect( $resetKinds, self::listOptionKinds() );
2711  $newOptions = array();
2712 
2713  // Use default values for the options that should be deleted, and
2714  // copy old values for the ones that shouldn't.
2715  foreach ( $this->mOptions as $key => $value ) {
2716  if ( in_array( $optionKinds[$key], $resetKinds ) ) {
2717  if ( array_key_exists( $key, $defaultOptions ) ) {
2718  $newOptions[$key] = $defaultOptions[$key];
2719  }
2720  } else {
2721  $newOptions[$key] = $value;
2722  }
2723  }
2724  }
2725 
2726  $this->mOptions = $newOptions;
2727  $this->mOptionsLoaded = true;
2728  }
2734  public function getDatePreference() {
2735  // Important migration for old data rows
2736  if ( is_null( $this->mDatePreference ) ) {
2737  global $wgLang;
2738  $value = $this->getOption( 'date' );
2739  $map = $wgLang->getDatePreferenceMigrationMap();
2740  if ( isset( $map[$value] ) ) {
2741  $value = $map[$value];
2742  }
2743  $this->mDatePreference = $value;
2744  }
2745  return $this->mDatePreference;
2746  }
2747 
2754  public function requiresHTTPS() {
2755  global $wgSecureLogin;
2756  if ( !$wgSecureLogin ) {
2757  return false;
2758  } else {
2759  $https = $this->getBoolOption( 'prefershttps' );
2760  wfRunHooks( 'UserRequiresHTTPS', array( $this, &$https ) );
2761  if ( $https ) {
2762  $https = wfCanIPUseHTTPS( $this->getRequest()->getIP() );
2763  }
2764  return $https;
2765  }
2766  }
2767 
2773  public function getStubThreshold() {
2774  global $wgMaxArticleSize; # Maximum article size, in Kb
2775  $threshold = $this->getIntOption( 'stubthreshold' );
2776  if ( $threshold > $wgMaxArticleSize * 1024 ) {
2777  // If they have set an impossible value, disable the preference
2778  // so we can use the parser cache again.
2779  $threshold = 0;
2780  }
2781  return $threshold;
2782  }
2788  public function getRights() {
2789  if ( is_null( $this->mRights ) ) {
2790  $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
2791  wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
2792  // Force reindexation of rights when a hook has unset one of them
2793  $this->mRights = array_values( array_unique( $this->mRights ) );
2794  }
2795  return $this->mRights;
2796  }
2797 
2803  public function getGroups() {
2804  $this->load();
2805  $this->loadGroups();
2806  return $this->mGroups;
2807  }
2808 
2816  public function getEffectiveGroups( $recache = false ) {
2817  if ( $recache || is_null( $this->mEffectiveGroups ) ) {
2818  wfProfileIn( __METHOD__ );
2819  $this->mEffectiveGroups = array_unique( array_merge(
2820  $this->getGroups(), // explicit groups
2821  $this->getAutomaticGroups( $recache ) // implicit groups
2822  ) );
2823  // Hook for additional groups
2824  wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
2825  // Force reindexation of groups when a hook has unset one of them
2826  $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
2827  wfProfileOut( __METHOD__ );
2828  }
2829  return $this->mEffectiveGroups;
2830  }
2831 
2839  public function getAutomaticGroups( $recache = false ) {
2840  if ( $recache || is_null( $this->mImplicitGroups ) ) {
2841  wfProfileIn( __METHOD__ );
2842  $this->mImplicitGroups = array( '*' );
2843  if ( $this->getId() ) {
2844  $this->mImplicitGroups[] = 'user';
2845 
2846  $this->mImplicitGroups = array_unique( array_merge(
2847  $this->mImplicitGroups,
2849  ) );
2850  }
2851  if ( $recache ) {
2852  // Assure data consistency with rights/groups,
2853  // as getEffectiveGroups() depends on this function
2854  $this->mEffectiveGroups = null;
2855  }
2856  wfProfileOut( __METHOD__ );
2857  }
2858  return $this->mImplicitGroups;
2859  }
2860 
2870  public function getFormerGroups() {
2871  if ( is_null( $this->mFormerGroups ) ) {
2872  $dbr = wfGetDB( DB_MASTER );
2873  $res = $dbr->select( 'user_former_groups',
2874  array( 'ufg_group' ),
2875  array( 'ufg_user' => $this->mId ),
2876  __METHOD__ );
2877  $this->mFormerGroups = array();
2878  foreach ( $res as $row ) {
2879  $this->mFormerGroups[] = $row->ufg_group;
2880  }
2881  }
2882  return $this->mFormerGroups;
2883  }
2889  public function getEditCount() {
2890  if ( !$this->getId() ) {
2891  return null;
2892  }
2893 
2894  if ( !isset( $this->mEditCount ) ) {
2895  /* Populate the count, if it has not been populated yet */
2896  wfProfileIn( __METHOD__ );
2897  $dbr = wfGetDB( DB_SLAVE );
2898  // check if the user_editcount field has been initialized
2899  $count = $dbr->selectField(
2900  'user', 'user_editcount',
2901  array( 'user_id' => $this->mId ),
2902  __METHOD__
2903  );
2904 
2905  if ( $count === null ) {
2906  // it has not been initialized. do so.
2907  $count = $this->initEditCount();
2908  }
2909  $this->mEditCount = $count;
2910  wfProfileOut( __METHOD__ );
2911  }
2912  return (int)$this->mEditCount;
2913  }
2914 
2920  public function addGroup( $group ) {
2921  if ( wfRunHooks( 'UserAddGroup', array( $this, &$group ) ) ) {
2922  $dbw = wfGetDB( DB_MASTER );
2923  if ( $this->getId() ) {
2924  $dbw->insert( 'user_groups',
2925  array(
2926  'ug_user' => $this->getID(),
2927  'ug_group' => $group,
2928  ),
2929  __METHOD__,
2930  array( 'IGNORE' ) );
2931  }
2932  }
2933  $this->loadGroups();
2934  $this->mGroups[] = $group;
2935  // In case loadGroups was not called before, we now have the right twice.
2936  // Get rid of the duplicate.
2937  $this->mGroups = array_unique( $this->mGroups );
2938 
2939  // Refresh the groups caches, and clear the rights cache so it will be
2940  // refreshed on the next call to $this->getRights().
2941  $this->getEffectiveGroups( true );
2942  $this->mRights = null;
2943 
2944  $this->invalidateCache();
2945  }
2946 
2952  public function removeGroup( $group ) {
2953  $this->load();
2954  if ( wfRunHooks( 'UserRemoveGroup', array( $this, &$group ) ) ) {
2955  $dbw = wfGetDB( DB_MASTER );
2956  $dbw->delete( 'user_groups',
2957  array(
2958  'ug_user' => $this->getID(),
2959  'ug_group' => $group,
2960  ), __METHOD__ );
2961  // Remember that the user was in this group
2962  $dbw->insert( 'user_former_groups',
2963  array(
2964  'ufg_user' => $this->getID(),
2965  'ufg_group' => $group,
2966  ),
2967  __METHOD__,
2968  array( 'IGNORE' ) );
2969  }
2970  $this->loadGroups();
2971  $this->mGroups = array_diff( $this->mGroups, array( $group ) );
2972 
2973  // Refresh the groups caches, and clear the rights cache so it will be
2974  // refreshed on the next call to $this->getRights().
2975  $this->getEffectiveGroups( true );
2976  $this->mRights = null;
2977 
2978  $this->invalidateCache();
2979  }
2985  public function isLoggedIn() {
2986  return $this->getID() != 0;
2987  }
2993  public function isAnon() {
2994  return !$this->isLoggedIn();
2995  }
2996 
3005  public function isAllowedAny( /*...*/ ) {
3006  $permissions = func_get_args();
3007  foreach ( $permissions as $permission ) {
3008  if ( $this->isAllowed( $permission ) ) {
3009  return true;
3010  }
3011  }
3012  return false;
3013  }
3014 
3020  public function isAllowedAll( /*...*/ ) {
3021  $permissions = func_get_args();
3022  foreach ( $permissions as $permission ) {
3023  if ( !$this->isAllowed( $permission ) ) {
3024  return false;
3025  }
3026  }
3027  return true;
3028  }
3029 
3035  public function isAllowed( $action = '' ) {
3036  if ( $action === '' ) {
3037  return true; // In the spirit of DWIM
3038  }
3039  // Patrolling may not be enabled
3040  if ( $action === 'patrol' || $action === 'autopatrol' ) {
3041  global $wgUseRCPatrol, $wgUseNPPatrol;
3042  if ( !$wgUseRCPatrol && !$wgUseNPPatrol ) {
3043  return false;
3044  }
3045  }
3046  // Use strict parameter to avoid matching numeric 0 accidentally inserted
3047  // by misconfiguration: 0 == 'foo'
3048  return in_array( $action, $this->getRights(), true );
3049  }
3055  public function useRCPatrol() {
3056  global $wgUseRCPatrol;
3057  return $wgUseRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
3058  }
3064  public function useNPPatrol() {
3065  global $wgUseRCPatrol, $wgUseNPPatrol;
3066  return (
3067  ( $wgUseRCPatrol || $wgUseNPPatrol )
3068  && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
3069  );
3070  }
3071 
3077  public function getRequest() {
3078  if ( $this->mRequest ) {
3079  return $this->mRequest;
3080  } else {
3081  global $wgRequest;
3082  return $wgRequest;
3083  }
3084  }
3085 
3092  public function getSkin() {
3093  wfDeprecated( __METHOD__, '1.18' );
3094  return RequestContext::getMain()->getSkin();
3095  }
3096 
3106  public function getWatchedItem( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
3107  $key = $checkRights . ':' . $title->getNamespace() . ':' . $title->getDBkey();
3108 
3109  if ( isset( $this->mWatchedItems[$key] ) ) {
3110  return $this->mWatchedItems[$key];
3111  }
3112 
3113  if ( count( $this->mWatchedItems ) >= self::MAX_WATCHED_ITEMS_CACHE ) {
3114  $this->mWatchedItems = array();
3115  }
3116 
3117  $this->mWatchedItems[$key] = WatchedItem::fromUserTitle( $this, $title, $checkRights );
3118  return $this->mWatchedItems[$key];
3119  }
3120 
3129  public function isWatched( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
3130  return $this->getWatchedItem( $title, $checkRights )->isWatched();
3131  }
3132 
3140  public function addWatch( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
3141  $this->getWatchedItem( $title, $checkRights )->addWatch();
3142  $this->invalidateCache();
3143  }
3144 
3152  public function removeWatch( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
3153  $this->getWatchedItem( $title, $checkRights )->removeWatch();
3154  $this->invalidateCache();
3155  }
3156 
3165  public function clearNotification( &$title, $oldid = 0 ) {
3166  global $wgUseEnotif, $wgShowUpdatedMarker;
3167 
3168  // Do nothing if the database is locked to writes
3169  if ( wfReadOnly() ) {
3170  return;
3171  }
3172 
3173  // Do nothing if not allowed to edit the watchlist
3174  if ( !$this->isAllowed( 'editmywatchlist' ) ) {
3175  return;
3176  }
3177 
3178  // If we're working on user's talk page, we should update the talk page message indicator
3179  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3180  if ( !wfRunHooks( 'UserClearNewTalkNotification', array( &$this, $oldid ) ) ) {
3181  return;
3182  }
3183 
3184  $nextid = $oldid ? $title->getNextRevisionID( $oldid ) : null;
3185 
3186  if ( !$oldid || !$nextid ) {
3187  // If we're looking at the latest revision, we should definitely clear it
3188  $this->setNewtalk( false );
3189  } else {
3190  // Otherwise we should update its revision, if it's present
3191  if ( $this->getNewtalk() ) {
3192  // Naturally the other one won't clear by itself
3193  $this->setNewtalk( false );
3194  $this->setNewtalk( true, Revision::newFromId( $nextid ) );
3195  }
3196  }
3197  }
3198 
3199  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3200  return;
3201  }
3202 
3203  if ( $this->isAnon() ) {
3204  // Nothing else to do...
3205  return;
3206  }
3207 
3208  // Only update the timestamp if the page is being watched.
3209  // The query to find out if it is watched is cached both in memcached and per-invocation,
3210  // and when it does have to be executed, it can be on a slave
3211  // If this is the user's newtalk page, we always update the timestamp
3212  $force = '';
3213  if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
3214  $force = 'force';
3215  }
3216 
3217  $this->getWatchedItem( $title )->resetNotificationTimestamp( $force, $oldid );
3218  }
3219 
3226  public function clearAllNotifications() {
3227  if ( wfReadOnly() ) {
3228  return;
3229  }
3230 
3231  // Do nothing if not allowed to edit the watchlist
3232  if ( !$this->isAllowed( 'editmywatchlist' ) ) {
3233  return;
3234  }
3235 
3236  global $wgUseEnotif, $wgShowUpdatedMarker;
3237  if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
3238  $this->setNewtalk( false );
3239  return;
3240  }
3241  $id = $this->getId();
3242  if ( $id != 0 ) {
3243  $dbw = wfGetDB( DB_MASTER );
3244  $dbw->update( 'watchlist',
3245  array( /* SET */ 'wl_notificationtimestamp' => null ),
3246  array( /* WHERE */ 'wl_user' => $id ),
3247  __METHOD__
3248  );
3249  // We also need to clear here the "you have new message" notification for the own user_talk page;
3250  // it's cleared one page view later in WikiPage::doViewUpdates().
3251  }
3252  }
3253 
3267  protected function setCookie( $name, $value, $exp = 0, $secure = null, $params = array() ) {
3268  $params['secure'] = $secure;
3269  $this->getRequest()->response()->setcookie( $name, $value, $exp, $params );
3270  }
3271 
3281  protected function clearCookie( $name, $secure = null, $params = array() ) {
3282  $this->setCookie( $name, '', time() - 86400, $secure, $params );
3283  }
3284 
3293  public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
3294  if ( $request === null ) {
3295  $request = $this->getRequest();
3296  }
3297 
3298  $this->load();
3299  if ( 0 == $this->mId ) {
3300  return;
3301  }
3302  if ( !$this->mToken ) {
3303  // When token is empty or NULL generate a new one and then save it to the database
3304  // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey
3305  // Simply by setting every cell in the user_token column to NULL and letting them be
3306  // regenerated as users log back into the wiki.
3307  $this->setToken();
3308  $this->saveSettings();
3309  }
3310  $session = array(
3311  'wsUserID' => $this->mId,
3312  'wsToken' => $this->mToken,
3313  'wsUserName' => $this->getName()
3314  );
3315  $cookies = array(
3316  'UserID' => $this->mId,
3317  'UserName' => $this->getName(),
3318  );
3319  if ( $rememberMe ) {
3320  $cookies['Token'] = $this->mToken;
3321  } else {
3322  $cookies['Token'] = false;
3323  }
3324 
3325  wfRunHooks( 'UserSetCookies', array( $this, &$session, &$cookies ) );
3326 
3327  foreach ( $session as $name => $value ) {
3328  $request->setSessionData( $name, $value );
3329  }
3330  foreach ( $cookies as $name => $value ) {
3331  if ( $value === false ) {
3332  $this->clearCookie( $name );
3333  } else {
3334  $this->setCookie( $name, $value, 0, $secure );
3335  }
3336  }
3337 
3345  if ( $request->getCheck( 'wpStickHTTPS' ) || $this->requiresHTTPS() ) {
3346  $this->setCookie(
3347  'forceHTTPS',
3348  'true',
3349  $rememberMe ? 0 : null,
3350  false,
3351  array( 'prefix' => '' ) // no prefix
3352  );
3353  }
3354  }
3355 
3359  public function logout() {
3360  if ( wfRunHooks( 'UserLogout', array( &$this ) ) ) {
3361  $this->doLogout();
3362  }
3363  }
3369  public function doLogout() {
3370  $this->clearInstanceCache( 'defaults' );
3371 
3372  $this->getRequest()->setSessionData( 'wsUserID', 0 );
3373 
3374  $this->clearCookie( 'UserID' );
3375  $this->clearCookie( 'Token' );
3376  $this->clearCookie( 'forceHTTPS', false, array( 'prefix' => '' ) );
3377 
3378  // Remember when user logged out, to prevent seeing cached pages
3379  $this->setCookie( 'LoggedOut', time(), time() + 86400 );
3380  }
3386  public function saveSettings() {
3387  global $wgAuth;
3388 
3389  $this->load();
3390  if ( wfReadOnly() ) {
3391  return;
3392  }
3393  if ( 0 == $this->mId ) {
3394  return;
3395  }
3396 
3397  $this->mTouched = self::newTouchedTimestamp();
3398  if ( !$wgAuth->allowSetLocalPassword() ) {
3399  $this->mPassword = '';
3400  }
3401 
3402  $dbw = wfGetDB( DB_MASTER );
3403  $dbw->update( 'user',
3404  array( /* SET */
3405  'user_name' => $this->mName,
3406  'user_password' => $this->mPassword,
3407  'user_newpassword' => $this->mNewpassword,
3408  'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
3409  'user_real_name' => $this->mRealName,
3410  'user_email' => $this->mEmail,
3411  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
3412  'user_touched' => $dbw->timestamp( $this->mTouched ),
3413  'user_token' => strval( $this->mToken ),
3414  'user_email_token' => $this->mEmailToken,
3415  'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
3416  'user_password_expires' => $dbw->timestampOrNull( $this->mPasswordExpires ),
3417  ), array( /* WHERE */
3418  'user_id' => $this->mId
3419  ), __METHOD__
3420  );
3421 
3422  $this->saveOptions();
3423 
3424  wfRunHooks( 'UserSaveSettings', array( $this ) );
3425  $this->clearSharedCache();
3426  $this->getUserPage()->invalidateCache();
3427  }
3433  public function idForName() {
3434  $s = trim( $this->getName() );
3435  if ( $s === '' ) {
3436  return 0;
3437  }
3438 
3439  $dbr = wfGetDB( DB_SLAVE );
3440  $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
3441  if ( $id === false ) {
3442  $id = 0;
3443  }
3444  return $id;
3445  }
3446 
3463  public static function createNew( $name, $params = array() ) {
3464  $user = new User;
3465  $user->load();
3466  $user->setToken(); // init token
3467  if ( isset( $params['options'] ) ) {
3468  $user->mOptions = $params['options'] + (array)$user->mOptions;
3469  unset( $params['options'] );
3470  }
3471  $dbw = wfGetDB( DB_MASTER );
3472  $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
3473 
3474  $fields = array(
3475  'user_id' => $seqVal,
3476  'user_name' => $name,
3477  'user_password' => $user->mPassword,
3478  'user_newpassword' => $user->mNewpassword,
3479  'user_newpass_time' => $dbw->timestampOrNull( $user->mNewpassTime ),
3480  'user_email' => $user->mEmail,
3481  'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
3482  'user_real_name' => $user->mRealName,
3483  'user_token' => strval( $user->mToken ),
3484  'user_registration' => $dbw->timestamp( $user->mRegistration ),
3485  'user_editcount' => 0,
3486  'user_touched' => $dbw->timestamp( self::newTouchedTimestamp() ),
3487  );
3488  foreach ( $params as $name => $value ) {
3489  $fields["user_$name"] = $value;
3490  }
3491  $dbw->insert( 'user', $fields, __METHOD__, array( 'IGNORE' ) );
3492  if ( $dbw->affectedRows() ) {
3493  $newUser = User::newFromId( $dbw->insertId() );
3494  } else {
3495  $newUser = null;
3496  }
3497  return $newUser;
3498  }
3499 
3526  public function addToDatabase() {
3527  $this->load();
3528  if ( !$this->mToken ) {
3529  $this->setToken(); // init token
3530  }
3531 
3532  $this->mTouched = self::newTouchedTimestamp();
3533 
3534  $dbw = wfGetDB( DB_MASTER );
3535  $inWrite = $dbw->writesOrCallbacksPending();
3536  $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
3537  $dbw->insert( 'user',
3538  array(
3539  'user_id' => $seqVal,
3540  'user_name' => $this->mName,
3541  'user_password' => $this->mPassword,
3542  'user_newpassword' => $this->mNewpassword,
3543  'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
3544  'user_email' => $this->mEmail,
3545  'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
3546  'user_real_name' => $this->mRealName,
3547  'user_token' => strval( $this->mToken ),
3548  'user_registration' => $dbw->timestamp( $this->mRegistration ),
3549  'user_editcount' => 0,
3550  'user_touched' => $dbw->timestamp( $this->mTouched ),
3551  ), __METHOD__,
3552  array( 'IGNORE' )
3553  );
3554  if ( !$dbw->affectedRows() ) {
3555  if ( !$inWrite ) {
3556  // XXX: Get out of REPEATABLE-READ so the SELECT below works.
3557  // Often this case happens early in views before any writes.
3558  // This shows up at least with CentralAuth.
3559  $dbw->commit( __METHOD__, 'flush' );
3560  }
3561  $this->mId = $dbw->selectField( 'user', 'user_id',
3562  array( 'user_name' => $this->mName ), __METHOD__ );
3563  $loaded = false;
3564  if ( $this->mId ) {
3565  if ( $this->loadFromDatabase() ) {
3566  $loaded = true;
3567  }
3568  }
3569  if ( !$loaded ) {
3570  throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
3571  "to insert user '{$this->mName}' row, but it was not present in select!" );
3572  }
3573  return Status::newFatal( 'userexists' );
3574  }
3575  $this->mId = $dbw->insertId();
3576 
3577  // Clear instance cache other than user table data, which is already accurate
3578  $this->clearInstanceCache();
3579 
3580  $this->saveOptions();
3581  return Status::newGood();
3582  }
3583 
3589  public function spreadAnyEditBlock() {
3590  if ( $this->isLoggedIn() && $this->isBlocked() ) {
3591  return $this->spreadBlock();
3592  }
3593  return false;
3594  }
3595 
3601  protected function spreadBlock() {
3602  wfDebug( __METHOD__ . "()\n" );
3603  $this->load();
3604  if ( $this->mId == 0 ) {
3605  return false;
3606  }
3607 
3608  $userblock = Block::newFromTarget( $this->getName() );
3609  if ( !$userblock ) {
3610  return false;
3611  }
3612 
3613  return (bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
3614  }
3620  public function isBlockedFromCreateAccount() {
3621  $this->getBlockedStatus();
3622  if ( $this->mBlock && $this->mBlock->prevents( 'createaccount' ) ) {
3623  return $this->mBlock;
3624  }
3625 
3626  # bug 13611: if the IP address the user is trying to create an account from is
3627  # blocked with createaccount disabled, prevent new account creation there even
3628  # when the user is logged in
3629  if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
3630  $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
3631  }
3632  return $this->mBlockedFromCreateAccount instanceof Block && $this->mBlockedFromCreateAccount->prevents( 'createaccount' )
3633  ? $this->mBlockedFromCreateAccount
3634  : false;
3635  }
3641  public function isBlockedFromEmailuser() {
3642  $this->getBlockedStatus();
3643  return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
3644  }
3650  public function isAllowedToCreateAccount() {
3651  return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
3652  }
3653 
3659  public function getUserPage() {
3660  return Title::makeTitle( NS_USER, $this->getName() );
3661  }
3662 
3668  public function getTalkPage() {
3669  $title = $this->getUserPage();
3670  return $title->getTalkPage();
3671  }
3672 
3678  public function isNewbie() {
3679  return !$this->isAllowed( 'autoconfirmed' );
3680  }
3681 
3687  public function checkPassword( $password ) {
3688  global $wgAuth, $wgLegacyEncoding;
3689  $this->load();
3690 
3691  // Certain authentication plugins do NOT want to save
3692  // domain passwords in a mysql database, so we should
3693  // check this (in case $wgAuth->strict() is false).
3694 
3695  if ( $wgAuth->authenticate( $this->getName(), $password ) ) {
3696  return true;
3697  } elseif ( $wgAuth->strict() ) {
3698  // Auth plugin doesn't allow local authentication
3699  return false;
3700  } elseif ( $wgAuth->strictUserAuth( $this->getName() ) ) {
3701  // Auth plugin doesn't allow local authentication for this user name
3702  return false;
3703  }
3704  if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
3705  return true;
3706  } elseif ( $wgLegacyEncoding ) {
3707  // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
3708  // Check for this with iconv
3709  $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
3710  if ( $cp1252Password != $password
3711  && self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId )
3712  ) {
3713  return true;
3714  }
3715  }
3716  return false;
3717  }
3718 
3727  public function checkTemporaryPassword( $plaintext ) {
3728  global $wgNewPasswordExpiry;
3729 
3730  $this->load();
3731  if ( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) {
3732  if ( is_null( $this->mNewpassTime ) ) {
3733  return true;
3734  }
3735  $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgNewPasswordExpiry;
3736  return ( time() < $expiry );
3737  } else {
3738  return false;
3739  }
3740  }
3741 
3750  public function editToken( $salt = '', $request = null ) {
3751  wfDeprecated( __METHOD__, '1.19' );
3752  return $this->getEditToken( $salt, $request );
3753  }
3754 
3767  public function getEditToken( $salt = '', $request = null ) {
3768  if ( $request == null ) {
3769  $request = $this->getRequest();
3770  }
3771 
3772  if ( $this->isAnon() ) {
3773  return EDIT_TOKEN_SUFFIX;
3774  } else {
3775  $token = $request->getSessionData( 'wsEditToken' );
3776  if ( $token === null ) {
3777  $token = MWCryptRand::generateHex( 32 );
3778  $request->setSessionData( 'wsEditToken', $token );
3779  }
3780  if ( is_array( $salt ) ) {
3781  $salt = implode( '|', $salt );
3782  }
3783  return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX;
3784  }
3785  }
3786 
3793  public static function generateToken() {
3794  return MWCryptRand::generateHex( 32 );
3795  }
3796 
3808  public function matchEditToken( $val, $salt = '', $request = null ) {
3809  $sessionToken = $this->getEditToken( $salt, $request );
3810  if ( $val != $sessionToken ) {
3811  wfDebug( "User::matchEditToken: broken session data\n" );
3812  }
3813  return $val == $sessionToken;
3814  }
3815 
3825  public function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
3826  $sessionToken = $this->getEditToken( $salt, $request );
3827  return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
3828  }
3829 
3837  public function sendConfirmationMail( $type = 'created' ) {
3838  global $wgLang;
3839  $expiration = null; // gets passed-by-ref and defined in next line.
3840  $token = $this->confirmationToken( $expiration );
3841  $url = $this->confirmationTokenUrl( $token );
3842  $invalidateURL = $this->invalidationTokenUrl( $token );
3843  $this->saveSettings();
3844 
3845  if ( $type == 'created' || $type === false ) {
3846  $message = 'confirmemail_body';
3847  } elseif ( $type === true ) {
3848  $message = 'confirmemail_body_changed';
3849  } else {
3850  // Messages: confirmemail_body_changed, confirmemail_body_set
3851  $message = 'confirmemail_body_' . $type;
3852  }
3853 
3854  return $this->sendMail( wfMessage( 'confirmemail_subject' )->text(),
3855  wfMessage( $message,
3856  $this->getRequest()->getIP(),
3857  $this->getName(),
3858  $url,
3859  $wgLang->timeanddate( $expiration, false ),
3860  $invalidateURL,
3861  $wgLang->date( $expiration, false ),
3862  $wgLang->time( $expiration, false ) )->text() );
3863  }
3864 
3875  public function sendMail( $subject, $body, $from = null, $replyto = null ) {
3876  if ( is_null( $from ) ) {
3877  global $wgPasswordSender;
3878  $sender = new MailAddress( $wgPasswordSender,
3879  wfMessage( 'emailsender' )->inContentLanguage()->text() );
3880  } else {
3881  $sender = new MailAddress( $from );
3882  }
3883 
3884  $to = new MailAddress( $this );
3885  return UserMailer::send( $to, $sender, $subject, $body, $replyto );
3886  }
3887 
3898  protected function confirmationToken( &$expiration ) {
3899  global $wgUserEmailConfirmationTokenExpiry;
3900  $now = time();
3901  $expires = $now + $wgUserEmailConfirmationTokenExpiry;
3902  $expiration = wfTimestamp( TS_MW, $expires );
3903  $this->load();
3904  $token = MWCryptRand::generateHex( 32 );
3905  $hash = md5( $token );
3906  $this->mEmailToken = $hash;
3907  $this->mEmailTokenExpires = $expiration;
3908  return $token;
3909  }
3910 
3916  protected function confirmationTokenUrl( $token ) {
3917  return $this->getTokenUrl( 'ConfirmEmail', $token );
3918  }
3919 
3925  protected function invalidationTokenUrl( $token ) {
3926  return $this->getTokenUrl( 'InvalidateEmail', $token );
3927  }
3928 
3943  protected function getTokenUrl( $page, $token ) {
3944  // Hack to bypass localization of 'Special:'
3945  $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
3946  return $title->getCanonicalURL();
3947  }
3948 
3956  public function confirmEmail() {
3957  // Check if it's already confirmed, so we don't touch the database
3958  // and fire the ConfirmEmailComplete hook on redundant confirmations.
3959  if ( !$this->isEmailConfirmed() ) {
3961  wfRunHooks( 'ConfirmEmailComplete', array( $this ) );
3962  }
3963  return true;
3964  }
3965 
3973  public function invalidateEmail() {
3974  $this->load();
3975  $this->mEmailToken = null;
3976  $this->mEmailTokenExpires = null;
3977  $this->setEmailAuthenticationTimestamp( null );
3978  wfRunHooks( 'InvalidateEmailComplete', array( $this ) );
3979  return true;
3980  }
3986  public function setEmailAuthenticationTimestamp( $timestamp ) {
3987  $this->load();
3988  $this->mEmailAuthenticated = $timestamp;
3989  wfRunHooks( 'UserSetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
3990  }
3991 
3997  public function canSendEmail() {
3998  global $wgEnableEmail, $wgEnableUserEmail;
3999  if ( !$wgEnableEmail || !$wgEnableUserEmail || !$this->isAllowed( 'sendemail' ) ) {
4000  return false;
4001  }
4002  $canSend = $this->isEmailConfirmed();
4003  wfRunHooks( 'UserCanSendEmail', array( &$this, &$canSend ) );
4004  return $canSend;
4005  }
4006 
4012  public function canReceiveEmail() {
4013  return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
4014  }
4015 
4026  public function isEmailConfirmed() {
4027  global $wgEmailAuthentication;
4028  $this->load();
4029  $confirmed = true;
4030  if ( wfRunHooks( 'EmailConfirmed', array( &$this, &$confirmed ) ) ) {
4031  if ( $this->isAnon() ) {
4032  return false;
4033  }
4034  if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4035  return false;
4036  }
4037  if ( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() ) {
4038  return false;
4039  }
4040  return true;
4041  } else {
4042  return $confirmed;
4043  }
4044  }
4050  public function isEmailConfirmationPending() {
4051  global $wgEmailAuthentication;
4052  return $wgEmailAuthentication &&
4053  !$this->isEmailConfirmed() &&
4054  $this->mEmailToken &&
4055  $this->mEmailTokenExpires > wfTimestamp();
4056  }
4057 
4065  public function getRegistration() {
4066  if ( $this->isAnon() ) {
4067  return false;
4068  }
4069  $this->load();
4070  return $this->mRegistration;
4071  }
4072 
4079  public function getFirstEditTimestamp() {
4080  if ( $this->getId() == 0 ) {
4081  return false; // anons
4082  }
4083  $dbr = wfGetDB( DB_SLAVE );
4084  $time = $dbr->selectField( 'revision', 'rev_timestamp',
4085  array( 'rev_user' => $this->getId() ),
4086  __METHOD__,
4087  array( 'ORDER BY' => 'rev_timestamp ASC' )
4088  );
4089  if ( !$time ) {
4090  return false; // no edits
4091  }
4092  return wfTimestamp( TS_MW, $time );
4093  }
4094 
4101  public static function getGroupPermissions( $groups ) {
4102  global $wgGroupPermissions, $wgRevokePermissions;
4103  $rights = array();
4104  // grant every granted permission first
4105  foreach ( $groups as $group ) {
4106  if ( isset( $wgGroupPermissions[$group] ) ) {
4107  $rights = array_merge( $rights,
4108  // array_filter removes empty items
4109  array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
4110  }
4111  }
4112  // now revoke the revoked permissions
4113  foreach ( $groups as $group ) {
4114  if ( isset( $wgRevokePermissions[$group] ) ) {
4115  $rights = array_diff( $rights,
4116  array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
4117  }
4118  }
4119  return array_unique( $rights );
4120  }
4121 
4128  public static function getGroupsWithPermission( $role ) {
4129  global $wgGroupPermissions;
4130  $allowedGroups = array();
4131  foreach ( array_keys( $wgGroupPermissions ) as $group ) {
4132  if ( self::groupHasPermission( $group, $role ) ) {
4133  $allowedGroups[] = $group;
4134  }
4135  }
4136  return $allowedGroups;
4137  }
4138 
4151  public static function groupHasPermission( $group, $role ) {
4152  global $wgGroupPermissions, $wgRevokePermissions;
4153  return isset( $wgGroupPermissions[$group][$role] ) && $wgGroupPermissions[$group][$role]
4154  && !( isset( $wgRevokePermissions[$group][$role] ) && $wgRevokePermissions[$group][$role] );
4155  }
4156 
4164  public static function isEveryoneAllowed( $right ) {
4165  global $wgGroupPermissions, $wgRevokePermissions;
4166  static $cache = array();
4167 
4168  // Use the cached results, except in unit tests which rely on
4169  // being able change the permission mid-request
4170  if ( isset( $cache[$right] ) && !defined( 'MW_PHPUNIT_TEST' ) ) {
4171  return $cache[$right];
4172  }
4173 
4174  if ( !isset( $wgGroupPermissions['*'][$right] ) || !$wgGroupPermissions['*'][$right] ) {
4175  $cache[$right] = false;
4176  return false;
4177  }
4178 
4179  // If it's revoked anywhere, then everyone doesn't have it
4180  foreach ( $wgRevokePermissions as $rights ) {
4181  if ( isset( $rights[$right] ) && $rights[$right] ) {
4182  $cache[$right] = false;
4183  return false;
4184  }
4185  }
4186 
4187  // Allow extensions (e.g. OAuth) to say false
4188  if ( !wfRunHooks( 'UserIsEveryoneAllowed', array( $right ) ) ) {
4189  $cache[$right] = false;
4190  return false;
4191  }
4192 
4193  $cache[$right] = true;
4194  return true;
4195  }
4196 
4203  public static function getGroupName( $group ) {
4204  $msg = wfMessage( "group-$group" );
4205  return $msg->isBlank() ? $group : $msg->text();
4206  }
4207 
4215  public static function getGroupMember( $group, $username = '#' ) {
4216  $msg = wfMessage( "group-$group-member", $username );
4217  return $msg->isBlank() ? $group : $msg->text();
4218  }
4219 
4226  public static function getAllGroups() {
4227  global $wgGroupPermissions, $wgRevokePermissions;
4228  return array_diff(
4229  array_merge( array_keys( $wgGroupPermissions ), array_keys( $wgRevokePermissions ) ),
4230  self::getImplicitGroups()
4231  );
4232  }
4238  public static function getAllRights() {
4239  if ( self::$mAllRights === false ) {
4240  global $wgAvailableRights;
4241  if ( count( $wgAvailableRights ) ) {
4242  self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
4243  } else {
4244  self::$mAllRights = self::$mCoreRights;
4245  }
4246  wfRunHooks( 'UserGetAllRights', array( &self::$mAllRights ) );
4247  }
4248  return self::$mAllRights;
4249  }
4255  public static function getImplicitGroups() {
4256  global $wgImplicitGroups;
4257  $groups = $wgImplicitGroups;
4258  wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) ); #deprecated, use $wgImplictGroups instead
4259  return $groups;
4260  }
4261 
4268  public static function getGroupPage( $group ) {
4269  $msg = wfMessage( 'grouppage-' . $group )->inContentLanguage();
4270  if ( $msg->exists() ) {
4271  $title = Title::newFromText( $msg->text() );
4272  if ( is_object( $title ) ) {
4273  return $title;
4274  }
4275  }
4276  return false;
4277  }
4278 
4287  public static function makeGroupLinkHTML( $group, $text = '' ) {
4288  if ( $text == '' ) {
4289  $text = self::getGroupName( $group );
4290  }
4291  $title = self::getGroupPage( $group );
4292  if ( $title ) {
4293  return Linker::link( $title, htmlspecialchars( $text ) );
4294  } else {
4295  return $text;
4296  }
4297  }
4298 
4307  public static function makeGroupLinkWiki( $group, $text = '' ) {
4308  if ( $text == '' ) {
4309  $text = self::getGroupName( $group );
4310  }
4311  $title = self::getGroupPage( $group );
4312  if ( $title ) {
4313  $page = $title->getPrefixedText();
4314  return "[[$page|$text]]";
4315  } else {
4316  return $text;
4317  }
4318  }
4319 
4329  public static function changeableByGroup( $group ) {
4330  global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
4331 
4332  $groups = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
4333  if ( empty( $wgAddGroups[$group] ) ) {
4334  // Don't add anything to $groups
4335  } elseif ( $wgAddGroups[$group] === true ) {
4336  // You get everything
4337  $groups['add'] = self::getAllGroups();
4338  } elseif ( is_array( $wgAddGroups[$group] ) ) {
4339  $groups['add'] = $wgAddGroups[$group];
4340  }
4341 
4342  // Same thing for remove
4343  if ( empty( $wgRemoveGroups[$group] ) ) {
4344  } elseif ( $wgRemoveGroups[$group] === true ) {
4345  $groups['remove'] = self::getAllGroups();
4346  } elseif ( is_array( $wgRemoveGroups[$group] ) ) {
4347  $groups['remove'] = $wgRemoveGroups[$group];
4348  }
4349 
4350  // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
4351  if ( empty( $wgGroupsAddToSelf['user'] ) || $wgGroupsAddToSelf['user'] !== true ) {
4352  foreach ( $wgGroupsAddToSelf as $key => $value ) {
4353  if ( is_int( $key ) ) {
4354  $wgGroupsAddToSelf['user'][] = $value;
4355  }
4356  }
4357  }
4358 
4359  if ( empty( $wgGroupsRemoveFromSelf['user'] ) || $wgGroupsRemoveFromSelf['user'] !== true ) {
4360  foreach ( $wgGroupsRemoveFromSelf as $key => $value ) {
4361  if ( is_int( $key ) ) {
4362  $wgGroupsRemoveFromSelf['user'][] = $value;
4363  }
4364  }
4365  }
4366 
4367  // Now figure out what groups the user can add to him/herself
4368  if ( empty( $wgGroupsAddToSelf[$group] ) ) {
4369  } elseif ( $wgGroupsAddToSelf[$group] === true ) {
4370  // No idea WHY this would be used, but it's there
4371  $groups['add-self'] = User::getAllGroups();
4372  } elseif ( is_array( $wgGroupsAddToSelf[$group] ) ) {
4373  $groups['add-self'] = $wgGroupsAddToSelf[$group];
4374  }
4375 
4376  if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
4377  } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
4378  $groups['remove-self'] = User::getAllGroups();
4379  } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
4380  $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
4381  }
4382 
4383  return $groups;
4384  }
4385 
4393  public function changeableGroups() {
4394  if ( $this->isAllowed( 'userrights' ) ) {
4395  // This group gives the right to modify everything (reverse-
4396  // compatibility with old "userrights lets you change
4397  // everything")
4398  // Using array_merge to make the groups reindexed
4399  $all = array_merge( User::getAllGroups() );
4400  return array(
4401  'add' => $all,
4402  'remove' => $all,
4403  'add-self' => array(),
4404  'remove-self' => array()
4405  );
4406  }
4407 
4408  // Okay, it's not so simple, we will have to go through the arrays
4409  $groups = array(
4410  'add' => array(),
4411  'remove' => array(),
4412  'add-self' => array(),
4413  'remove-self' => array()
4414  );
4415  $addergroups = $this->getEffectiveGroups();
4416 
4417  foreach ( $addergroups as $addergroup ) {
4418  $groups = array_merge_recursive(
4419  $groups, $this->changeableByGroup( $addergroup )
4420  );
4421  $groups['add'] = array_unique( $groups['add'] );
4422  $groups['remove'] = array_unique( $groups['remove'] );
4423  $groups['add-self'] = array_unique( $groups['add-self'] );
4424  $groups['remove-self'] = array_unique( $groups['remove-self'] );
4425  }
4426  return $groups;
4427  }
4433  public function incEditCount() {
4434  if ( !$this->isAnon() ) {
4435  $dbw = wfGetDB( DB_MASTER );
4436  $dbw->update(
4437  'user',
4438  array( 'user_editcount=user_editcount+1' ),
4439  array( 'user_id' => $this->getId() ),
4440  __METHOD__
4441  );
4442 
4443  // Lazy initialization check...
4444  if ( $dbw->affectedRows() == 0 ) {
4445  // Now here's a goddamn hack...
4446  $dbr = wfGetDB( DB_SLAVE );
4447  if ( $dbr !== $dbw ) {
4448  // If we actually have a slave server, the count is
4449  // at least one behind because the current transaction
4450  // has not been committed and replicated.
4451  $this->initEditCount( 1 );
4452  } else {
4453  // But if DB_SLAVE is selecting the master, then the
4454  // count we just read includes the revision that was
4455  // just added in the working transaction.
4456  $this->initEditCount();
4457  }
4458  }
4459  }
4460  // edit count in user cache too
4461  $this->invalidateCache();
4462  }
4463 
4470  protected function initEditCount( $add = 0 ) {
4471  // Pull from a slave to be less cruel to servers
4472  // Accuracy isn't the point anyway here
4473  $dbr = wfGetDB( DB_SLAVE );
4474  $count = (int)$dbr->selectField(
4475  'revision',
4476  'COUNT(rev_user)',
4477  array( 'rev_user' => $this->getId() ),
4478  __METHOD__
4479  );
4480  $count = $count + $add;
4481 
4482  $dbw = wfGetDB( DB_MASTER );
4483  $dbw->update(
4484  'user',
4485  array( 'user_editcount' => $count ),
4486  array( 'user_id' => $this->getId() ),
4487  __METHOD__
4488  );
4489 
4490  return $count;
4491  }
4492 
4499  public static function getRightDescription( $right ) {
4500  $key = "right-$right";
4501  $msg = wfMessage( $key );
4502  return $msg->isBlank() ? $right : $msg->text();
4503  }
4504 
4512  public static function oldCrypt( $password, $userId ) {
4513  global $wgPasswordSalt;
4514  if ( $wgPasswordSalt ) {
4515  return md5( $userId . '-' . md5( $password ) );
4516  } else {
4517  return md5( $password );
4518  }
4519  }
4520 
4529  public static function crypt( $password, $salt = false ) {
4530  global $wgPasswordSalt;
4531 
4532  $hash = '';
4533  if ( !wfRunHooks( 'UserCryptPassword', array( &$password, &$salt, &$wgPasswordSalt, &$hash ) ) ) {
4534  return $hash;
4535  }
4536 
4537  if ( $wgPasswordSalt ) {
4538  if ( $salt === false ) {
4539  $salt = MWCryptRand::generateHex( 8 );
4540  }
4541  return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
4542  } else {
4543  return ':A:' . md5( $password );
4544  }
4545  }
4546 
4557  public static function comparePasswords( $hash, $password, $userId = false ) {
4558  $type = substr( $hash, 0, 3 );
4559 
4560  $result = false;
4561  if ( !wfRunHooks( 'UserComparePasswords', array( &$hash, &$password, &$userId, &$result ) ) ) {
4562  return $result;
4563  }
4564 
4565  if ( $type == ':A:' ) {
4566  // Unsalted
4567  return md5( $password ) === substr( $hash, 3 );
4568  } elseif ( $type == ':B:' ) {
4569  // Salted
4570  list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
4571  return md5( $salt . '-' . md5( $password ) ) === $realHash;
4572  } else {
4573  // Old-style
4574  return self::oldCrypt( $password, $userId ) === $hash;
4575  }
4576  }
4577 
4599  public function addNewUserLogEntry( $action = false, $reason = '' ) {
4600  global $wgUser, $wgNewUserLog;
4601  if ( empty( $wgNewUserLog ) ) {
4602  return true; // disabled
4603  }
4604 
4605  if ( $action === true ) {
4606  $action = 'byemail';
4607  } elseif ( $action === false ) {
4608  if ( $this->getName() == $wgUser->getName() ) {
4609  $action = 'create';
4610  } else {
4611  $action = 'create2';
4612  }
4613  }
4614 
4615  if ( $action === 'create' || $action === 'autocreate' ) {
4616  $performer = $this;
4617  } else {
4618  $performer = $wgUser;
4619  }
4620 
4621  $logEntry = new ManualLogEntry( 'newusers', $action );
4622  $logEntry->setPerformer( $performer );
4623  $logEntry->setTarget( $this->getUserPage() );
4624  $logEntry->setComment( $reason );
4625  $logEntry->setParameters( array(
4626  '4::userid' => $this->getId(),
4627  ) );
4628  $logid = $logEntry->insert();
4629 
4630  if ( $action !== 'autocreate' ) {
4631  $logEntry->publish( $logid );
4632  }
4633 
4634  return (int)$logid;
4635  }
4636 
4644  public function addNewUserLogEntryAutoCreate() {
4645  $this->addNewUserLogEntry( 'autocreate' );
4646 
4647  return true;
4648  }
4649 
4655  protected function loadOptions( $data = null ) {
4657 
4658  $this->load();
4659 
4660  if ( $this->mOptionsLoaded ) {
4661  return;
4662  }
4663 
4664  $this->mOptions = self::getDefaultOptions();
4665 
4666  if ( !$this->getId() ) {
4667  // For unlogged-in users, load language/variant options from request.
4668  // There's no need to do it for logged-in users: they can set preferences,
4669  // and handling of page content is done by $pageLang->getPreferredVariant() and such,
4670  // so don't override user's choice (especially when the user chooses site default).
4671  $variant = $wgContLang->getDefaultVariant();
4672  $this->mOptions['variant'] = $variant;
4673  $this->mOptions['language'] = $variant;
4674  $this->mOptionsLoaded = true;
4675  return;
4676  }
4677 
4678  // Maybe load from the object
4679  if ( !is_null( $this->mOptionOverrides ) ) {
4680  wfDebug( "User: loading options for user " . $this->getId() . " from override cache.\n" );
4681  foreach ( $this->mOptionOverrides as $key => $value ) {
4682  $this->mOptions[$key] = $value;
4683  }
4684  } else {
4685  if ( !is_array( $data ) ) {
4686  wfDebug( "User: loading options for user " . $this->getId() . " from database.\n" );
4687  // Load from database
4688  $dbr = wfGetDB( DB_SLAVE );
4689 
4690  $res = $dbr->select(
4691  'user_properties',
4692  array( 'up_property', 'up_value' ),
4693  array( 'up_user' => $this->getId() ),
4694  __METHOD__
4695  );
4696 
4697  $this->mOptionOverrides = array();
4698  $data = array();
4699  foreach ( $res as $row ) {
4700  $data[$row->up_property] = $row->up_value;
4701  }
4702  }
4703  foreach ( $data as $property => $value ) {
4704  $this->mOptionOverrides[$property] = $value;
4705  $this->mOptions[$property] = $value;
4706  }
4707  }
4708 
4709  $this->mOptionsLoaded = true;
4710 
4711  wfRunHooks( 'UserLoadOptions', array( $this, &$this->mOptions ) );
4712  }
4713 
4717  protected function saveOptions() {
4718  $this->loadOptions();
4719 
4720  // Not using getOptions(), to keep hidden preferences in database
4721  $saveOptions = $this->mOptions;
4722 
4723  // Allow hooks to abort, for instance to save to a global profile.
4724  // Reset options to default state before saving.
4725  if ( !wfRunHooks( 'UserSaveOptions', array( $this, &$saveOptions ) ) ) {
4726  return;
4727  }
4728 
4729  $userId = $this->getId();
4730  $insert_rows = array();
4731  foreach ( $saveOptions as $key => $value ) {
4732  // Don't bother storing default values
4733  $defaultOption = self::getDefaultOption( $key );
4734  if ( ( is_null( $defaultOption ) &&
4735  !( $value === false || is_null( $value ) ) ) ||
4736  $value != $defaultOption
4737  ) {
4738  $insert_rows[] = array(
4739  'up_user' => $userId,
4740  'up_property' => $key,
4741  'up_value' => $value,
4742  );
4743  }
4744  }
4745 
4746  $dbw = wfGetDB( DB_MASTER );
4747  // Find and delete any prior preference rows...
4748  $res = $dbw->select( 'user_properties',
4749  array( 'up_property' ), array( 'up_user' => $userId ), __METHOD__ );
4750  $priorKeys = array();
4751  foreach ( $res as $row ) {
4752  $priorKeys[] = $row->up_property;
4753  }
4754  if ( count( $priorKeys ) ) {
4755  // Do the DELETE by PRIMARY KEY for prior rows.
4756  // In the past a very large portion of calls to this function are for setting
4757  // 'rememberpassword' for new accounts (a preference that has since been removed).
4758  // Doing a blanket per-user DELETE for new accounts with no rows in the table
4759  // caused gap locks on [max user ID,+infinity) which caused high contention since
4760  // updates would pile up on each other as they are for higher (newer) user IDs.
4761  // It might not be necessary these days, but it shouldn't hurt either.
4762  $dbw->delete( 'user_properties',
4763  array( 'up_user' => $userId, 'up_property' => $priorKeys ), __METHOD__ );
4764  }
4765  // Insert the new preference rows
4766  $dbw->insert( 'user_properties', $insert_rows, __METHOD__, array( 'IGNORE' ) );
4767  }
4768 
4792  public static function passwordChangeInputAttribs() {
4793  global $wgMinimalPasswordLength;
4794 
4795  if ( $wgMinimalPasswordLength == 0 ) {
4796  return array();
4797  }
4798 
4799  # Note that the pattern requirement will always be satisfied if the
4800  # input is empty, so we need required in all cases.
4801  #
4802  # @todo FIXME: Bug 23769: This needs to not claim the password is required
4803  # if e-mail confirmation is being used. Since HTML5 input validation
4804  # is b0rked anyway in some browsers, just return nothing. When it's
4805  # re-enabled, fix this code to not output required for e-mail
4806  # registration.
4807  #$ret = array( 'required' );
4808  $ret = array();
4809 
4810  # We can't actually do this right now, because Opera 9.6 will print out
4811  # the entered password visibly in its error message! When other
4812  # browsers add support for this attribute, or Opera fixes its support,
4813  # we can add support with a version check to avoid doing this on Opera
4814  # versions where it will be a problem. Reported to Opera as
4815  # DSK-262266, but they don't have a public bug tracker for us to follow.
4816  /*
4817  if ( $wgMinimalPasswordLength > 1 ) {
4818  $ret['pattern'] = '.{' . intval( $wgMinimalPasswordLength ) . ',}';
4819  $ret['title'] = wfMessage( 'passwordtooshort' )
4820  ->numParams( $wgMinimalPasswordLength )->text();
4821  }
4822  */
4823 
4824  return $ret;
4825  }
4826 
4832  public static function selectFields() {
4833  return array(
4834  'user_id',
4835  'user_name',
4836  'user_real_name',
4837  'user_password',
4838  'user_newpassword',
4839  'user_newpass_time',
4840  'user_email',
4841  'user_touched',
4842  'user_token',
4843  'user_email_authenticated',
4844  'user_email_token',
4845  'user_email_token_expires',
4846  'user_password_expires',
4847  'user_registration',
4848  'user_editcount',
4849  );
4850  }
4851 
4859  static function newFatalPermissionDeniedStatus( $permission ) {
4860  global $wgLang;
4861 
4862  $groups = array_map(
4863  array( 'User', 'makeGroupLinkWiki' ),
4864  User::getGroupsWithPermission( $permission )
4865  );
4866 
4867  if ( $groups ) {
4868  return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
4869  } else {
4870  return Status::newFatal( 'badaccess-group0' );
4871  }
4872  }
4873 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1383
User\addWatch
addWatch( $title, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Watch an article.
Definition: User.php:3135
User\$mHash
$mHash
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\saveOptions
saveOptions()
Definition: User.php:4712
User\$mBlockreason
$mBlockreason
Bool Whether the cache variables have been loaded.
Definition: User.php:219
Block\prevents
prevents( $action, $x=null)
Get/set whether the Block prevents a given action.
Definition: Block.php:886
User\load
load()
Load the user table data for this object from the source given by mFrom.
Definition: User.php:269
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:4787
User\loadFromId
loadFromId()
Load user table data, given mId has already been set.
Definition: User.php:312
Preferences\getPreferences
static getPreferences( $user, IContextSource $context)
Definition: Preferences.php:78
User\updateNewtalk
updateNewtalk( $field, $id, $curRev=null)
Add or update the new messages flag.
Definition: User.php:2036
User\$mEmailToken
$mEmailToken
Bool Whether the cache variables have been loaded.
Definition: User.php:185
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
User\getNewtalk
getNewtalk()
Check if the user has new messages.
Definition: User.php:1920
User\addGroup
addGroup( $group)
Add the user to the given group.
Definition: User.php:2915
UserMailer\send
static send( $to, $from, $subject, $body, $replyto=null, $contentType='text/plain;charset=UTF-8')
This function will perform a direct (authenticated) login to a SMTP Server to use for mail relaying i...
Definition: UserMailer.php:163
User\$mOptionsLoaded
$mOptionsLoaded
Bool Whether the cache variables have been loaded.
Definition: User.php:197
$wgUser
$wgUser
Definition: Setup.php:552
User\inDnsBlacklist
inDnsBlacklist( $ip, $bases)
Whether the given IP is in a given DNS blacklist.
Definition: User.php:1514
$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. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag '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 '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. '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 '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 '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 wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() '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 User::isValidEmailAddr(), 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. '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 '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) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':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:1528
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1358
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:411
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:189
User\$mEditCount
$mEditCount
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\confirmationTokenUrl
confirmationTokenUrl( $token)
Return a URL the user can use to confirm their email address.
Definition: User.php:3911
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
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:4129
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
User\isValidPassword
isValidPassword( $password)
Is the input a valid password for this user?
Definition: User.php:690
User\getId
getId()
Get the user's ID.
Definition: User.php:1852
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:88
User\makeGroupLinkWiki
static makeGroupLinkWiki( $group, $text='')
Create a link to the group in Wikitext, if available; else return the group name.
Definition: User.php:4302
User\isAnon
isAnon()
Get whether the user is anonymous.
Definition: User.php:2988
User\$mBlock
Block $mBlock
Bool Whether the cache variables have been loaded.
Definition: User.php:229
User\$mEffectiveGroups
$mEffectiveGroups
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\getTokenUrl
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
Definition: User.php:3938
User\MAX_WATCHED_ITEMS_CACHE
const MAX_WATCHED_ITEMS_CACHE
Maximum items in $mWatchedItems.
Definition: User.php:71
User\isLocallyBlockedProxy
static isLocallyBlockedProxy( $ip)
Check if an IP address is in the local proxy list.
Definition: User.php:1561
User\resetTokenFromOption
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition: User.php:2546
User\loadFromUserObject
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition: User.php:1244
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:4854
User\getPasswordExpireDate
getPasswordExpireDate()
Get this user's password expiration date.
Definition: User.php:833
User\isValidEmailAddr
static isValidEmailAddr( $addr)
Does a string look like an e-mail address?
Definition: User.php:865
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:2884
User\spreadBlock
spreadBlock()
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition: User.php:3596
User\incEditCount
incEditCount()
Increment the user's edit-count field.
Definition: User.php:4428
User\newFromSession
static newFromSession(WebRequest $request=null)
Create a new user object using data from session or cookies.
Definition: User.php:449
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
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:2602
Block\chooseBlock
static chooseBlock(array $blocks, array $ipChain)
From a list of multiple blocks, find the most exact and strongest Block.
Definition: Block.php:1089
User\getBlock
getBlock( $bFromSlave=true)
Get the block affecting the user, or null if the user is not blocked.
Definition: User.php:1731
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3650
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:4045
$timestamp
if( $limit) $timestamp
Definition: importImages.php:104
User\getBlockId
getBlockId()
If user is blocked, return the ID for the block.
Definition: User.php:1784
User\getIntOption
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition: User.php:2489
SearchEngine\searchableNamespaces
static searchableNamespaces()
Make a list of searchable namespaces and their canonical names.
Definition: SearchEngine.php:334
User\__construct
__construct()
Lightweight constructor for an anonymous user.
Definition: User.php:255
User\editToken
editToken( $salt='', $request=null)
Alias for getEditToken.
Definition: User.php:3745
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
User\getToken
getToken( $forceCreation=true)
Get the user's current token.
Definition: User.php:2265
User\spreadAnyEditBlock
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition: User.php:3584
User\$mEmail
$mEmail
Bool Whether the cache variables have been loaded.
Definition: User.php:185
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1040
User\getNewMessageRevisionId
getNewMessageRevisionId()
Get the revision ID for the last talk page revision viewed by the talk page owner.
Definition: User.php:1991
User\loadDefaults
loadDefaults( $name=false)
Set cached properties to default.
Definition: User.php:970
User\$mAllowUsertalk
bool $mAllowUsertalk
Bool Whether the cache variables have been loaded.
Definition: User.php:233
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$defaultOptions
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks this Boolean value will be checked to determine if the password was valid return false to implement your own hashing method this String will be used as the hash which may be added to this hook is run right before returning the options to the caller which means it s potentially called dozens or hundreds of times You may want to cache the results of non trivial operations in your hook function for this reason & $defaultOptions
Definition: hooks.txt:2697
User\$mNewtalk
$mNewtalk
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition: User.php:219
User\loadOptions
loadOptions( $data=null)
Load the user options either from cache, the database or an array.
Definition: User.php:4650
User\makeGroupLinkHTML
static makeGroupLinkHTML( $group, $text='')
Create a link to the group in HTML, if available; else return the group name.
Definition: User.php:4282
$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:1530
$from
$from
Definition: importImages.php:90
User\setNewpassword
setNewpassword( $str, $throttle=true)
Set the password for a password reminder or new account email.
Definition: User.php:2295
Autopromote\getAutopromoteGroups
static getAutopromoteGroups(User $user)
Get the groups for the given user based on $wgAutopromote.
Definition: Autopromote.php:35
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2798
User\edits
static edits( $uid)
Count the number of edits of a user.
Definition: User.php:939
User\setEmailWithConfirmation
setEmailWithConfirmation( $str)
Set the user's e-mail address and a confirmation mail if needed.
Definition: User.php:2365
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
User\getStubThreshold
getStubThreshold()
Get the user preferred stub threshold.
Definition: User.php:2768
Sanitizer\validateEmail
static validateEmail( $addr)
Does a string look like an e-mail address?
Definition: Sanitizer.php:1830
User\comparePasswords
static comparePasswords( $hash, $password, $userId=false)
Compare a password hash with a plain-text password.
Definition: User.php:4552
$params
$params
Definition: styleTest.css.php:40
$limit
if( $sleep) $limit
Definition: importImages.php:99
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:970
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1313
PasswordError
Thrown by User::setPassword() on error.
Definition: User.php:45
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:388
User\loadFromRow
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition: User.php:1172
User\setEmailAuthenticationTimestamp
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition: User.php:3981
IP\isIPv6
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
Definition: IP.php:85
User\clearCookie
clearCookie( $name, $secure=null, $params=array())
Clear a cookie on the user's client.
Definition: User.php:3276
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:3654
User\USER_TOKEN_LENGTH
const USER_TOKEN_LENGTH
Global constants made accessible as class constants so that autoloader magic can be used.
Definition: User.php:64
User\getGroups
getGroups()
Get the list of explicit group memberships this user has.
Definition: User.php:2798
$s
$s
Definition: mergeMessageFileList.php:156
User\$mOptionOverrides
$mOptionOverrides
Bool Whether the cache variables have been loaded.
Definition: User.php:185
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:514
User\useNPPatrol
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition: User.php:3059
User\$mToken
$mToken
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:2729
User\setEmail
setEmail( $str)
Set the user's e-mail address.
Definition: User.php:2348
$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
User\isValidUserName
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:569
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:3832
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4146
User\getEmailAuthenticationTimestamp
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition: User.php:2338
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:1615
User\$mFrom
$mFrom
String Initialization data source if mLoadedItems!==true.
Definition: User.php:214
User\initEditCount
initEditCount( $add=0)
Initialize user_editcount from data out of the revision table.
Definition: User.php:4465
User\useRCPatrol
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition: User.php:3050
User\invalidateEmail
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
Definition: User.php:3968
$messages
$messages
Definition: LogTests.i18n.php:8
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:470
User\loadGroups
loadGroups()
Load the groups from the database if they aren't already loaded.
Definition: User.php:1256
User\$mNewpassTime
$mNewpassTime
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\compareSecrets
compareSecrets( $answer, $test)
A comparison of two strings, not vulnerable to timing attacks.
Definition: User.php:1115
User\deleteNewtalk
deleteNewtalk( $field, $id)
Clear the new messages flag for the given user.
Definition: User.php:2061
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:1530
User\$mCacheVars
static $mCacheVars
Array of Strings List of member variables which are saved to the shared cache (memcached).
Definition: User.php:79
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:2783
User\getRequest
getRequest()
Get the WebRequest object to use with this object.
Definition: User.php:3072
Preferences\getSaveBlacklist
static getSaveBlacklist()
Definition: Preferences.php:68
User\getAutomaticGroups
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:2834
User\generateToken
static generateToken()
Generate a looking random token for various uses.
Definition: User.php:3788
User\setPassword
setPassword( $str)
Set the password and reset the random token.
Definition: User.php:2208
User\$mRights
$mRights
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\$mNewpassword
$mNewpassword
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1350
$dbr
$dbr
Definition: testCompression.php:48
Linker\link
static link( $target, $html=null, $customAttribs=array(), $query=array(), $options=array())
This function returns an HTML link to the given target.
Definition: Linker.php:192
User\isBlockedFrom
isBlockedFrom( $title, $bFromSlave=false)
Check if user is blocked from editing a particular article.
Definition: User.php:1743
User\resetOptions
resetOptions( $resetKinds=array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused'), IContextSource $context=null)
Reset certain (or all) options to the site defaults.
Definition: User.php:2686
NS_MAIN
const NS_MAIN
Definition: Defines.php:79
MailAddress
Stores a single person's name and email address.
Definition: UserMailer.php:32
User\getEmail
getEmail()
Get the user's e-mail address.
Definition: User.php:2328
file
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing we can concentrate it all in an extension file
Definition: hooks.txt:93
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:3663
$test
$test
Definition: Utf8Test.php:89
User\$mName
$mName
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\matchEditToken
matchEditToken( $val, $salt='', $request=null)
Check given value against the token value stored in the session.
Definition: User.php:3803
User\$mBlockedby
$mBlockedby
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:3521
User\clearSharedCache
clearSharedCache()
Clear user data from memcached.
Definition: User.php:2131
User\invalidateCache
invalidateCache()
Immediately touch the user data cache for this account.
Definition: User.php:2144
User\setInternalPassword
setInternalPassword( $str)
Set the password and reset the random token unconditionally.
Definition: User.php:2246
User\getWatchedItem
getWatchedItem( $title, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Get a WatchedItem for this user and $title.
Definition: User.php:3101
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:3920
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3571
User\isLocked
isLocked()
Check if user account is locked.
Definition: User.php:1818
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1127
WatchedItem\fromUserTitle
static fromUserTitle( $user, $title, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Create a WatchedItem object with the given user and title.
Definition: WatchedItem.php:56
$property
$property
Definition: styleTest.css.php:44
User\checkPasswordValidity
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition: User.php:729
User\$mOptions
$mOptions
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\confirmEmail
confirmEmail()
Mark the e-mail address confirmed.
Definition: User.php:3951
User\setItemLoaded
setItemLoaded( $item)
Set that an item has been loaded.
Definition: User.php:1025
User\blockedFor
blockedFor()
If user is blocked, return the specified reason for the block.
Definition: User.php:1775
User\$mImplicitGroups
$mImplicitGroups
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\MW_USER_VERSION
const MW_USER_VERSION
Definition: User.php:65
User\getOptions
getOptions()
Get all user's options.
Definition: User.php:2450
User\setNewtalk
setNewtalk( $val, $curRev=null)
Update the 'You have new messages!' status.
Definition: User.php:2080
User\isWatched
isWatched( $title, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Check the watched status of an article.
Definition: User.php:3124
User\$mBlockedFromCreateAccount
Block $mBlockedFromCreateAccount
Bool Whether the cache variables have been loaded.
Definition: User.php:237
User\isAllowedToCreateAccount
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition: User.php:3645
User\logout
logout()
Log this user out.
Definition: User.php:3354
User\confirmationToken
confirmationToken(&$expiration)
Generate, store, and return a new e-mail confirmation code.
Definition: User.php:3893
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:2501
User\getImplicitGroups
static getImplicitGroups()
Get a list of implicit groups.
Definition: User.php:4250
User\$mPasswordExpires
$mPasswordExpires
Bool Whether the cache variables have been loaded.
Definition: User.php:190
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
User\isHidden
isHidden()
Check if user account is hidden.
Definition: User.php:1834
User\randomPassword
static randomPassword()
Return a random password.
Definition: User.php:950
wfMessage
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 an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables 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\isBlockedFromEmailuser
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition: User.php:3636
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4001
User\isIP
static isIP( $name)
Does the string match an anonymous IPv4 address?
Definition: User.php:554
User\validateCache
validateCache( $timestamp)
Validate the cache for this account.
Definition: User.php:2178
User\getEffectiveGroups
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:2811
User\isPingLimitable
isPingLimitable()
Is this user subject to rate limiting?
Definition: User.php:1593
User\removeGroup
removeGroup( $group)
Remove the user from the given group.
Definition: User.php:2947
User\newFromConfirmationCode
static newFromConfirmationCode( $code)
Factory method to fetch whichever user has a given email confirmation code.
Definition: User.php:429
User\canReceiveEmail
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition: User.php:4007
User\isNewbie
isNewbie()
Determine whether the user is a newbie.
Definition: User.php:3673
User\isAllowedAll
isAllowedAll()
Is the input a valid password for this user?
Definition: User.php:3015
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
User\checkNewtalk
checkNewtalk( $field, $id, $fromMaster=false)
Internal uncached check for new messages.
Definition: User.php:2018
User\clearInstanceCache
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition: User.php:1325
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2514
$cookies
return false to override stock group removal can be modified modifiable will be added to $_SESSION & $cookies
Definition: hooks.txt:2838
$columns
if(! $in) $columns
Definition: Utf8Test.php:50
User\$mId
$mId
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\setName
setName( $str)
Set the user name.
Definition: User.php:1903
User\setCookie
setCookie( $name, $value, $exp=0, $secure=null, $params=array())
Set a cookie on the user's client.
Definition: User.php:3262
User\createNew
static createNew( $name, $params=array())
Add a user to the database, return the user object.
Definition: User.php:3458
User\$mLoadedItems
$mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition: User.php:202
$allowUsertalk
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks this Boolean value will be checked to determine if the password was valid return false to implement your own hashing method this String will be used as the hash which may be added to this hook is run right before returning the options to the caller which means it s potentially called dozens or hundreds of times You may want to cache the results of non trivial operations in your hook function for this reason change this to override email change this to override email authentication timestamp whether or not the user is blocked from that page & $allowUsertalk
Definition: hooks.txt:2697
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
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:188
User\clearNotification
clearNotification(&$title, $oldid=0)
Clear the user's notification timestamp for the given title.
Definition: User.php:3160
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:3381
User\$mGroups
$mGroups
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\addNewUserLogEntry
addNewUserLogEntry( $action=false, $reason='')
Add a newuser log entry for this user.
Definition: User.php:4594
User\$mPassword
$mPassword
Bool Whether the cache variables have been loaded.
Definition: User.php:185
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:1010
User\$mCoreRights
static $mCoreRights
Array of Strings Core rights.
Definition: User.php:108
User\getFirstEditTimestamp
getFirstEditTimestamp()
Get the timestamp of the first edit.
Definition: User.php:4074
User\expirePassword
expirePassword( $ts=0)
Expire a user's password.
Definition: User.php:776
$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:1530
User\setRealName
setRealName( $str)
Set the user's real name.
Definition: User.php:2410
$ok
$ok
Definition: UtfNormalTest.php:71
User\EDIT_TOKEN_SUFFIX
const EDIT_TOKEN_SUFFIX
Definition: User.php:66
User\getNewMessageLinks
getNewMessageLinks()
Return the data needed to construct links for new talk page message alerts.
Definition: User.php:1968
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2431
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
User\getBlockedStatus
getBlockedStatus( $bFromSlave=true)
Get blocking information.
Definition: User.php:1399
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3604
User\idForName
idForName()
If only this user's username is known, and it exists, return the user ID.
Definition: User.php:3428
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:82
User\getFormerGroups
getFormerGroups()
Returns the groups the user has belonged to.
Definition: User.php:2865
User\loadFromDatabase
loadFromDatabase()
Load user and user_group data from the database.
Definition: User.php:1134
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:483
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
User\newTouchedTimestamp
static newTouchedTimestamp()
Generate a current or new-future timestamp to be stored in the user_touched field when we update thin...
Definition: User.php:2119
$value
$value
Definition: styleTest.css.php:45
User\loadFromSession
loadFromSession()
Load user data from the session or login cookie.
Definition: User.php:1035
User\isBlockedGlobally
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
Definition: User.php:1797
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:2425
User\isDnsBlacklisted
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
Definition: User.php:1491
User\getTouched
getTouched()
Get the user touched timestamp.
Definition: User.php:2187
IP\parseRange
static parseRange( $range)
Given a string range in a number of formats, return the start and end of the range in hexadecimal.
Definition: IP.php:564
User\__toString
__toString()
Definition: User.php:262
User\$mRegistration
$mRegistration
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\getRealName
getRealName()
Get the user's real name.
Definition: User.php:2398
User\$idCacheByName
static $idCacheByName
Bool Whether the cache variables have been loaded.
Definition: User.php:243
Block\setBlocker
setBlocker( $user)
Set the user who implemented (or will implement) this block.
Definition: Block.php:1306
User\setCookies
setCookies( $request=null, $secure=null, $rememberMe=false)
Set the default cookies for this session on the user's client.
Definition: User.php:3288
User\getGroupPermissions
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
Definition: User.php:4096
User\getGroupPage
static getGroupPage( $group)
Get the title of a page describing a particular group.
Definition: User.php:4263
User\matchEditTokenNoSuffix
matchEditTokenNoSuffix( $val, $salt='', $request=null)
Check given value against the token value stored in the session, ignoring the suffix.
Definition: User.php:3820
User\changeableGroups
changeableGroups()
Returns an array of groups that this user can add and remove.
Definition: User.php:4388
User\clearAllNotifications
clearAllNotifications()
Resets all of the given user's page-change notification timestamps.
Definition: User.php:3221
WatchedItem\CHECK_USER_RIGHTS
const CHECK_USER_RIGHTS
Constant to specify that user rights 'editmywatchlist' and 'viewmywatchlist' should be checked.
Definition: WatchedItem.php:42
User\isPasswordReminderThrottled
isPasswordReminderThrottled()
Has password reminder email been sent within the last $wgPasswordReminderResendTime hours?
Definition: User.php:2314
User\crypt
static crypt( $password, $salt=false)
Make a new-style password hash.
Definition: User.php:4524
User\checkPassword
checkPassword( $password)
Check to see if the given clear-text password is one of the accepted passwords.
Definition: User.php:3682
User\getAllGroups
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4221
User\getAllRights
static getAllRights()
Get a list of all available permissions.
Definition: User.php:4233
User\$mTouched
$mTouched
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\$mEmailAuthenticated
$mEmailAuthenticated
Bool Whether the cache variables have been loaded.
Definition: User.php:185
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:420
User\blockedBy
blockedBy()
If user is blocked, return the name of the user who placed the block.
Definition: User.php:1766
$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:237
User\getGroupMember
static getGroupMember( $group, $username='#')
Get the localized descriptive name for a member of a group, if it exists.
Definition: User.php:4210
IP\isIPv4
static isIPv4( $ip)
Given a string, determine if it as valid IP in IPv4 only.
Definition: IP.php:96
IContextSource
Interface for objects which can provide a context on request.
Definition: IContextSource.php:29
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
Definition: WebRequest.php:38
$hash
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks & $hash
Definition: hooks.txt:2697
$password
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks this Boolean value will be checked to determine if the password was valid return false to implement your own hashing method & $password
Definition: hooks.txt:2697
$summary
$summary
Definition: importImages.php:120
User\changeableByGroup
static changeableByGroup( $group)
Returns an array of the groups that a particular group can add/remove.
Definition: User.php:4324
User\$mWatchedItems
Array $mWatchedItems
Bool Whether the cache variables have been loaded.
Definition: User.php:241
User\getGroupName
static getGroupName( $group)
Get the localized descriptive name for a group, if it exists.
Definition: User.php:4198
User\resetPasswordExpiration
resetPasswordExpiration( $load=true)
Clear the password expiration for a user.
Definition: User.php:788
$count
$count
Definition: UtfNormalTest2.php:96
$wgUseEnotif
$wgUseEnotif
Definition: Setup.php:288
Autopromote\getAutopromoteOnceGroups
static getAutopromoteOnceGroups(User $user, $event)
Get the groups for the given user based on the given criteria.
Definition: Autopromote.php:63
$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:1337
User\setId
setId( $v)
Set the user and reload all fields according to a given ID.
Definition: User.php:1867
User\getRegistration
getRegistration()
Get the timestamp of account creation.
Definition: User.php:4060
IP\sanitizeIP
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
Definition: IP.php:134
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users have the given permission.
Definition: User.php:4159
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
$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\idFromName
static idFromName( $name)
Get database id given a user name.
Definition: User.php:502
User\isAllowedAny
isAllowedAny()
Check if user is allowed to access a feature / make an action.
Definition: User.php:3000
User\getRightDescription
static getRightDescription( $right)
Get the description of a given right.
Definition: User.php:4494
$cache
$cache
Definition: mcc.php:32
User\isLoggedIn
isLoggedIn()
Get whether the user is logged in.
Definition: User.php:2980
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:3722
User\getTitleKey
getTitleKey()
Get the user's name escaped by underscores.
Definition: User.php:1912
User\saveToCache
saveToCache()
Save user data to the shared cache.
Definition: User.php:351
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2279
in
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
Definition: maintenance.txt:1
wfBaseConvert
wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true, $engine='auto')
Convert an arbitrarily-long digit string from one numeric base to another, optionally zero-padding to...
Definition: GlobalFunctions.php:3368
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:534
TS_UNIX
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
Definition: GlobalFunctions.php:2426
User\$mEmailTokenExpires
$mEmailTokenExpires
Bool Whether the cache variables have been loaded.
Definition: User.php:185
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:883
User\isEmailConfirmed
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition: User.php:4021
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
Block
Definition: Block.php:22
UserCache\singleton
static singleton()
Definition: UserCache.php:34
StubObject\unstub
static unstub( $obj)
Unstubs an object, if it is a stub object.
Definition: StubObject.php:80
$incrBy
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled $incrBy
Definition: hooks.txt:1956
Revision\loadFromTimestamp
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition: Revision.php:277
User\getPasswordExpired
getPasswordExpired()
Check if the user's password is expired.
Definition: User.php:814
User
User
Definition: All_system_messages.txt:425
$keys
$keys
Definition: testCompression.php:63
NS_USER
const NS_USER
Definition: Defines.php:81
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1284
User\sendMail
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition: User.php:3870
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:2523
ManualLogEntry
Class for creating log entries manually, for example to inject them into the database.
Definition: LogEntry.php:339
User\$mFormerGroups
$mFormerGroups
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\getSkin
getSkin()
Get the current skin, loading it if required.
Definition: User.php:3087
User\$mBlockedGlobally
$mBlockedGlobally
Bool Whether the cache variables have been loaded.
Definition: User.php:219
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:601
User\selectFields
static selectFields()
Return the list of user fields that should be selected to create a new user object.
Definition: User.php:4827
$t
$t
Definition: testCompression.php:65
User\addNewUserLogEntryAutoCreate
addNewUserLogEntryAutoCreate()
Add an autocreate newuser log entry for this user Used by things like CentralAuth and perhaps other a...
Definition: User.php:4639
User\$mRequest
WebRequest $mRequest
Bool Whether the cache variables have been loaded.
Definition: User.php:225
User\getBoolOption
getBoolOption( $oname)
Get the user's current setting for a given option, as a boolean value.
Definition: User.php:2477
User\removeWatch
removeWatch( $title, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Stop watching an article.
Definition: User.php:3147
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2573
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:623
User\$mDatePreference
$mDatePreference
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\isBlocked
isBlocked( $bFromSlave=true)
Check if user is blocked.
Definition: User.php:1721
User\$mRealName
$mRealName
Bool Whether the cache variables have been loaded.
Definition: User.php:185
User\isBlockedFromCreateAccount
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
Definition: User.php:3615
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:3762
User\requiresHTTPS
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition: User.php:2749
User\$mAllRights
static $mAllRights
String Cached results of getAllRights()
Definition: User.php:181
User\isItemLoaded
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition: User.php:1015
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:2503
$res
$res
Definition: database.txt:21
User\getPasswordValidity
getPasswordValidity( $password)
Given unvalidated password input, return error message on failure.
Definition: User.php:702
User\whoIsReal
static whoIsReal( $id)
Get the real name of a user given their user ID.
Definition: User.php:493
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:1876
User\doLogout
doLogout()
Clear the user's cookies and session, and reset the instance cache.
Definition: User.php:3364
User\canSendEmail
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition: User.php:3992
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:660
User\$mHideName
$mHideName
Bool Whether the cache variables have been loaded.
Definition: User.php:219
User\oldCrypt
static oldCrypt( $password, $userId)
Make an old-style password hash.
Definition: User.php:4507
User\$mLocked
$mLocked
Bool Whether the cache variables have been loaded.
Definition: User.php:219
IP\isIPAddress
static isIPAddress( $ip)
Determine if a string is as valid IP address or network (CIDR prefix).
Definition: IP.php:74
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4123
User\isAllowed
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3030
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63
User\listOptionKinds
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
Definition: User.php:2580
$changed
$changed
Definition: UtfNormalGenerate.php:130
page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk page
Definition: hooks.txt:1956
$type
$type
Definition: testCompression.php:46