'reupload',
'reupload-shared',
'rollback',
- 'selenium',
'sendemail',
'siteadmin',
'suppressionlog',
var $mId, $mName, $mRealName, $mPassword, $mNewpassword, $mNewpassTime,
$mEmail, $mTouched, $mToken, $mEmailAuthenticated,
$mEmailToken, $mEmailTokenExpires, $mRegistration, $mGroups, $mOptionOverrides,
- $mCookiePassword;
+ $mCookiePassword, $mEditCount, $mAllowUsertalk;
//@}
/**
* Lazy-initialized variables, invalidated with clearInstanceCache
*/
var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mRights,
- $mBlockreason, $mEffectiveGroups, $mFormerGroups, $mBlockedGlobally,
- $mLocked, $mHideName, $mOptions;
+ $mBlockreason, $mEffectiveGroups, $mImplicitGroups, $mFormerGroups, $mBlockedGlobally,
+ $mLocked, $mHideName, $mOptions, $mDisplayName;
/**
* @var WebRequest
* @var Block
*/
var $mBlock;
+
+ /**
+ * @var Block
+ */
private $mBlockedFromCreateAccount = false;
static $idCacheByName = array();
$this->clearInstanceCache( 'defaults' );
}
+ /**
+ * @return String
+ */
function __toString(){
return $this->getName();
}
* Load user table data, given mId has already been set.
* @return Bool false if the ID does not exist, true otherwise
*/
- private function loadFromId() {
+ public function loadFromId() {
global $wgMemc;
if ( $this->mId == 0 ) {
$this->loadDefaults();
//@}
-
/**
* Get the username corresponding to a given user ID
* @param $id Int User ID
- * @return String The corresponding username
+ * @return String|false The corresponding username
*/
- static function whoIs( $id ) {
+ public static function whoIs( $id ) {
$dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), __METHOD__ );
}
* Get the real name of a user given their user ID
*
* @param $id Int User ID
- * @return String The corresponding user's real name
+ * @return String|false The corresponding user's real name
*/
public static function whoIsReal( $id ) {
$dbr = wfGetDB( DB_SLAVE );
* - 'valid' Valid for batch processes
* - 'usable' Valid for batch processes and login
* - 'creatable' Valid for batch processes, login and account creation
+ *
+ * @return bool|string
*/
public static function getCanonicalName( $name, $validate = 'valid' ) {
# Force usernames to capital
*
* @note This no longer clears uncached lazy-initialised properties;
* the constructor does that instead.
+ *
+ * @param $name string
*/
- private function loadDefaults( $name = false ) {
+ public function loadDefaults( $name = false ) {
wfProfileIn( __METHOD__ );
$this->mId = 0;
*
* @return Bool True if the user exists, false if the user is anonymous
*/
- private function loadFromDatabase() {
+ public function loadFromDatabase() {
# Paranoia
$this->mId = intval( $this->mId );
public function loadFromRow( $row ) {
$all = true;
+ $this->mGroups = null; // deferred
+
if ( isset( $row->user_name ) ) {
$this->mName = $row->user_name;
$this->mFrom = 'name';
$all = false;
}
+ if ( isset( $row->user_editcount ) ) {
+ $this->mEditCount = $row->user_editcount;
+ } else {
+ $all = false;
+ }
+
if ( isset( $row->user_password ) ) {
$this->mPassword = $row->user_password;
$this->mNewpassword = $row->user_newpassword;
$this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time );
$this->mEmail = $row->user_email;
- $this->decodeOptions( $row->user_options );
- $this->mTouched = wfTimestamp(TS_MW,$row->user_touched);
+ if ( isset( $row->user_options ) ) {
+ $this->decodeOptions( $row->user_options );
+ }
+ $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
$this->mToken = $row->user_token;
$this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
$this->mEmailToken = $row->user_email_token;
$this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
$this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
- $this->mEditCount = $row->user_editcount;
} else {
$all = false;
}
$log->addEntry( 'autopromote',
$this->getUserPage(),
'', // no comment
+ // These group names are "list to texted"-ed in class LogPage.
array( implode( ', ', $oldGroups ), implode( ', ', $newGroups ) )
);
}
/**
* Clear various cached data stored in this object.
- * @param $reloadFrom String Reload user and user_groups table data from a
+ * @param $reloadFrom bool|String Reload user and user_groups table data from a
* given source. May be "name", "id", "defaults", "session", or false for
* no reload.
*/
$this->mHash = false;
$this->mRights = null;
$this->mEffectiveGroups = null;
+ $this->mImplicitGroups = null;
$this->mOptions = null;
+ $this->mDisplayName = null;
if ( $reloadFrom ) {
$this->mLoadedItems = array();
}
$defOpt['skin'] = $wgDefaultSkin;
+ // FIXME: Ideally we'd cache the results of this function so the hook is only run once,
+ // but that breaks the parser tests because they rely on being able to change $wgContLang
+ // mid-request and see that change reflected in the return value of this function.
+ // Which is insane and would never happen during normal MW operation, but is also not
+ // likely to get fixed unless and until we context-ify everything.
+ // See also https://www.mediawiki.org/wiki/Special:Code/MediaWiki/101488#c25275
+ wfRunHooks( 'UserGetDefaultOptions', array( &$defOpt ) );
+
return $defOpt;
}
# user is not immune to autoblocks/hardblocks, and they are the current user so we
# know which IP address they're actually coming from
if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->getID() == $wgUser->getID() ) {
- $ip = wfGetIP();
+ $ip = $this->getRequest()->getIP();
} else {
$ip = null;
}
$this->mBlockreason = $this->mBlock->mReason;
$this->mHideName = $this->mBlock->mHideName;
$this->mAllowUsertalk = !$this->mBlock->prevents( 'editownusertalk' );
- if ( $this->isLoggedIn() && $wgUser->getID() == $this->getID() ) {
- $this->spreadBlock();
- }
}
# Proxy blocking
foreach( (array)$bases as $base ) {
# Make hostname
- $host = "$ipReversed.$base";
+ # If we have an access key, use that too (ProjectHoneypot, etc.)
+ if( is_array( $base ) ) {
+ if( count( $base ) >= 2 ) {
+ # Access key is 1, base URL is 0
+ $host = "{$base[1]}.$ipReversed.{$base[0]}";
+ } else {
+ $host = "$ipReversed.{$base[0]}";
+ }
+ } else {
+ $host = "$ipReversed.$base";
+ }
# Send query
$ipList = gethostbynamel( $host );
/**
* Check if an IP address is in the local proxy list
+ *
+ * @param $ip string
+ *
* @return bool
*/
public static function isLocallyBlockedProxy( $ip ) {
*/
public function isPingLimitable() {
global $wgRateLimitsExcludedIPs;
- if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) {
+ if( in_array( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
// No other good way currently to disable rate limits
// for specific IPs. :P
// But this is a crappy hack and should die.
$limits = $wgRateLimits[$action];
$keys = array();
$id = $this->getId();
- $ip = wfGetIP();
+ $ip = $this->getRequest()->getIP();
$userLimit = false;
if( isset( $limits['anon'] ) && $id == 0 ) {
* @return Bool True if blocked, false otherwise
*/
public function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
+ return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
+ }
+
+ /**
+ * Get the block affecting the user, or null if the user is not blocked
+ *
+ * @param $bFromSlave Bool Whether to check the slave database instead of the master
+ * @return Block|null
+ */
+ public function getBlock( $bFromSlave = true ){
$this->getBlockedStatus( $bFromSlave );
- return $this->mBlock instanceof Block && $this->mBlock->prevents( 'edit' );
+ return $this->mBlock instanceof Block ? $this->mBlock : null;
}
/**
if( IP::isIPAddress( $this->getName() ) ) {
$ip = $this->getName();
} elseif( !$ip ) {
- $ip = wfGetIP();
+ $ip = $this->getRequest()->getIP();
}
$blocked = false;
wfRunHooks( 'UserIsBlockedGlobally', array( &$this, $ip, &$blocked ) );
$this->load();
if ( $this->mName === false ) {
# Clean up IPs
- $this->mName = IP::sanitizeIP( wfGetIP() );
+ $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
}
return $this->mName;
}
/**
* Validate the cache for this account.
* @param $timestamp String A timestamp in TS_MW format
+ *
+ * @return bool
*/
public function validateCache( $timestamp ) {
$this->load();
*
* @param $str String New password to set
* @throws PasswordError on failure
+ *
+ * @return bool
*/
public function setPassword( $str ) {
global $wgAuth;
* Set the random token (used for persistent authentication)
* Called from loadDefaults() among other places.
*
- * @param $token String If specified, set the token to this value
+ * @param $token String|bool If specified, set the token to this value
*/
public function setToken( $token = false ) {
global $wgSecretKey, $wgProxyKey;
$this->mRealName = $str;
}
+ /**
+ * Return the name of this user we should used to display in the user interface
+ * @return String The user's display name
+ */
+ public function getDisplayName() {
+ global $wgRealNameInInterface;
+ if ( is_null( $this->mDisplayName ) ) {
+ $displayName = null;
+
+ // Allow hooks to set a display name
+ wfRunHooks( 'UserDisplayName', array( $this, &$displayName ) );
+
+ if ( is_null( $displayName ) && $wgRealNameInInterface && $this->getRealName() ) {
+ // If $wgRealNameInInterface is true use the real name as the display name if it's set
+ $displayName = $this->getRealName();
+ }
+
+ if ( is_null( $displayName ) ) {
+ $displayName = $this->getName();
+ }
+
+ $this->mDisplayName = $displayName;
+ }
+ return $this->mDisplayName;
+ }
+
/**
* Get the user's current setting for a given option.
*
/**
* Get the user preferred stub threshold
+ *
+ * @return int
*/
public function getStubThreshold() {
global $wgMaxArticleSize; # Maximum article size, in Kb
*/
public function getGroups() {
$this->load();
+ $this->loadGroups();
return $this->mGroups;
}
public function getEffectiveGroups( $recache = false ) {
if ( $recache || is_null( $this->mEffectiveGroups ) ) {
wfProfileIn( __METHOD__ );
- $this->mEffectiveGroups = $this->getGroups();
- $this->mEffectiveGroups[] = '*';
- if( $this->getId() ) {
- $this->mEffectiveGroups[] = 'user';
+ $this->mEffectiveGroups = array_unique( array_merge(
+ $this->getGroups(), // explicit groups
+ $this->getAutomaticGroups( $recache ) // implicit groups
+ ) );
+ # Hook for additional groups
+ wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
+ wfProfileOut( __METHOD__ );
+ }
+ return $this->mEffectiveGroups;
+ }
- $this->mEffectiveGroups = array_unique( array_merge(
- $this->mEffectiveGroups,
+ /**
+ * Get the list of implicit group memberships this user has.
+ * This includes 'user' if logged in, '*' for all accounts,
+ * and autopromoted groups
+ * @param $recache Bool Whether to avoid the cache
+ * @return Array of String internal group names
+ */
+ public function getAutomaticGroups( $recache = false ) {
+ if ( $recache || is_null( $this->mImplicitGroups ) ) {
+ wfProfileIn( __METHOD__ );
+ $this->mImplicitGroups = array( '*' );
+ if ( $this->getId() ) {
+ $this->mImplicitGroups[] = 'user';
+
+ $this->mImplicitGroups = array_unique( array_merge(
+ $this->mImplicitGroups,
Autopromote::getAutopromoteGroups( $this )
) );
-
- # Hook for additional groups
- wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
+ }
+ if ( $recache ) {
+ # Assure data consistency with rights/groups,
+ # as getEffectiveGroups() depends on this function
+ $this->mEffectiveGroups = null;
}
wfProfileOut( __METHOD__ );
}
- return $this->mEffectiveGroups;
+ return $this->mImplicitGroups;
}
/**
/**
* Check if user is allowed to access a feature / make an action
- * @param varargs String permissions to test
+ *
+ * @internal param \String $varargs permissions to test
* @return Boolean: True if user is allowed to perform *any* of the given actions
+ *
+ * @return bool
*/
public function isAllowedAny( /*...*/ ){
$permissions = func_get_args();
}
/**
- * @param varargs String
+ *
+ * @internal param $varargs string
* @return bool True if the user is allowed to perform *all* of the given actions
*/
public function isAllowedAll( /*...*/ ){
/**
* Internal mechanics of testing a permission
* @param $action String
- * @paramn $ns int Namespace optional
+ * @param $ns int|null Namespace optional
* @return bool
*/
public function isAllowed( $action = '', $ns = null ) {
$this->invalidateCache();
}
+ /**
+ * Cleans up watchlist by removing invalid entries from it
+ */
+ public function cleanupWatchlist() {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->delete( 'watchlist', array( 'wl_namespace < 0', 'wl_user' => $this->getId() ), __METHOD__ );
+ }
+
/**
* Clear the user's notification timestamp for the given title.
* If e-notif e-mails are on, they will receive notification mails on
/**
* Set this user's options from an encoded string
* @param $str String Encoded options to import
+ *
+ * @deprecated in 1.19 due to removal of user_options from the user table
*/
private function decodeOptions( $str ) {
if( !$str )
* Clear the user's cookies and session, and reset the instance cache.
* @see logout()
*/
- private function doLogout() {
+ public function doLogout() {
$this->clearInstanceCache( 'defaults' );
$this->getRequest()->setSessionData( 'wsUserID', 0 );
'user_real_name' => $this->mRealName,
'user_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
- 'user_options' => '',
'user_touched' => $dbw->timestamp( $this->mTouched ),
'user_token' => $this->mToken,
'user_email_token' => $this->mEmailToken,
'user_email' => $user->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
'user_real_name' => $user->mRealName,
- 'user_options' => '',
'user_token' => $user->mToken,
'user_registration' => $dbw->timestamp( $user->mRegistration ),
'user_editcount' => 0,
'user_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_real_name' => $this->mRealName,
- 'user_options' => '',
'user_token' => $this->mToken,
'user_registration' => $dbw->timestamp( $this->mRegistration ),
'user_editcount' => 0,
}
/**
- * If this (non-anonymous) user is blocked, block any IP address
- * they've successfully logged in from.
+ * If this user is logged-in and blocked,
+ * block any IP address they've successfully logged in from.
+ * @return bool A block was spread
*/
- public function spreadBlock() {
+ public function spreadAnyEditBlock() {
+ if ( $this->isLoggedIn() && $this->isBlocked() ) {
+ return $this->spreadBlock();
+ }
+ return false;
+ }
+
+ /**
+ * If this (non-anonymous) user is blocked,
+ * block the IP address they've successfully logged in from.
+ * @return bool A block was spread
+ */
+ protected function spreadBlock() {
wfDebug( __METHOD__ . "()\n" );
$this->load();
if ( $this->mId == 0 ) {
- return;
+ return false;
}
$userblock = Block::newFromTarget( $this->getName() );
if ( !$userblock ) {
- return;
+ return false;
}
- $userblock->doAutoblock( wfGetIP() );
+ return (bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
}
/**
# blocked with createaccount disabled, prevent new account creation there even
# when the user is logged in
if( $this->mBlockedFromCreateAccount === false ){
- $this->mBlockedFromCreateAccount = Block::newFromTarget( null, wfGetIP() );
+ $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
}
return $this->mBlockedFromCreateAccount instanceof Block && $this->mBlockedFromCreateAccount->prevents( 'createaccount' )
? $this->mBlockedFromCreateAccount
/**
* Check if the given clear-text password matches the temporary password
* sent by e-mail for password reset operations.
+ *
+ * @param $plaintext string
+ *
* @return Boolean: True if matches, false otherwise
*/
public function checkTemporaryPassword( $plaintext ) {
}
}
+ /**
+ * Alias for getEditToken.
+ * @deprecated since 1.19, use getEditToken instead.
+ *
+ * @param $salt String|Array of Strings Optional function-specific data for hashing
+ * @param $request WebRequest object to use or null to use $wgRequest
+ * @return String The new edit token
+ */
+ public function editToken( $salt = '', $request = null ) {
+ return $this->getEditToken( $salt, $request );
+ }
+
/**
* Initialize (if necessary) and return a session token value
* which can be used in edit forms to show that the user's
* login credentials aren't being hijacked with a foreign form
* submission.
*
+ * @since 1.19
+ *
* @param $salt String|Array of Strings Optional function-specific data for hashing
* @param $request WebRequest object to use or null to use $wgRequest
* @return String The new edit token
*/
- public function editToken( $salt = '', $request = null ) {
+ public function getEditToken( $salt = '', $request = null ) {
if ( $request == null ) {
$request = $this->getRequest();
}
* @return Boolean: Whether the token matches
*/
public function matchEditToken( $val, $salt = '', $request = null ) {
- $sessionToken = $this->editToken( $salt, $request );
+ $sessionToken = $this->getEditToken( $salt, $request );
if ( $val != $sessionToken ) {
wfDebug( "User::matchEditToken: broken session data\n" );
}
* @return Boolean: Whether the token matches
*/
public function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
- $sessionToken = $this->editToken( $salt, $request );
+ $sessionToken = $this->getEditToken( $salt, $request );
return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
}
return $this->sendMail( wfMsg( 'confirmemail_subject' ),
wfMsg( $message,
- wfGetIP(),
+ $this->getRequest()->getIP(),
$this->getName(),
$url,
$wgLang->timeanddate( $expiration, false ),
* @note Call saveSettings() after calling this function to commit
* this change to the database.
*
- * @param[out] &$expiration \mixed Accepts the expiration time
+ * @param &$expiration \mixed Accepts the expiration time
* @return String New token
*/
private function confirmationToken( &$expiration ) {
/**
* Internal function to format the e-mail validation/invalidation URLs.
- * This uses $wgArticlePath directly as a quickie hack to use the
+ * This uses a quickie hack to use the
* hardcoded English names of the Special: pages, for ASCII safety.
*
* @note Since these URLs get dropped directly into emails, using the
* @return String Formatted URL
*/
protected function getTokenUrl( $page, $token ) {
- global $wgArticlePath;
- return wfExpandUrl(
- str_replace(
- '$1',
- "Special:$page/$token",
- $wgArticlePath ) );
+ // Hack to bypass localization of 'Special:'
+ $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
+ return $title->getCanonicalUrl();
}
/**
* Mark the e-mail address confirmed.
*
* @note Call saveSettings() after calling this function to commit the change.
+ *
+ * @return true
*/
public function confirmEmail() {
$this->setEmailAuthenticationTimestamp( wfTimestampNow() );
* Get the permissions associated with a given list of groups
*
* @param $groups Array of Strings List of internal group names
+ * @param $ns int
+ *
* @return Array of Strings List of permission key names for given groups combined
*/
- public static function getGroupPermissions( $groups, $ns = null ) {
+ public static function getGroupPermissions( array $groups, $ns = null ) {
global $wgGroupPermissions, $wgRevokePermissions;
$rights = array();
/**
* Helper for User::getGroupPermissions
- * @param array $list
- * @param int $ns
+ * @param $list array
+ * @param $ns int
* @return array
*/
private static function extractRights( $list, $ns ) {
* Get all the groups who have a given permission
*
* @param $role String Role to check
+ * @param $ns int
+ *
+ *
* @return Array of Strings List of internal group names with the given permission
*/
public static function getGroupsWithPermission( $role, $ns = null ) {
* Get the localized descriptive name for a member of a group, if it exists
*
* @param $group String Internal group name
+ * @param $username String Username for gender (since 1.19)
* @return String Localized name for group member
*/
- public static function getGroupMember( $group ) {
- $msg = wfMessage( "group-$group-member" );
+ public static function getGroupMember( $group, $username = '#' ) {
+ $msg = wfMessage( "group-$group-member", $username );
return $msg->isBlank() ? $group : $msg->text();
}
* Make a new-style password hash
*
* @param $password String Plain-text password
- * @param $salt String Optional salt, may be random or the user ID.
+ * @param bool|string $salt Optional salt, may be random or the user ID.
+
* If unspecified or false, will generate one automatically
* @return String Password hash
*/
*
* @param $hash String Password hash
* @param $password String Plain-text password to compare
- * @param $userId String User ID for old-style password salt
- * @return Boolean:
+ * @param $userId String|bool User ID for old-style password salt
+ *
+ * @return Boolean
*/
public static function comparePasswords( $hash, $password, $userId = false ) {
$type = substr( $hash, 0, 3 );
* @param $byEmail Boolean: account made by email?
* @param $reason String: user supplied reason
*
- * @return true
+ * @return int|bool True if not $wgNewUserLog; otherwise ID of log item or 0 on failure
*/
public function addNewUserLogEntry( $byEmail = false, $reason = '' ) {
global $wgUser, $wgContLang, $wgNewUserLog;
}
}
$log = new LogPage( 'newusers' );
- $log->addEntry(
+ return (int)$log->addEntry(
$action,
$this->getUserPage(),
$reason,
array( $this->getId() )
);
- return true;
}
/**
return true;
}
+ /**
+ * @todo document
+ */
protected function loadOptions() {
$this->load();
if ( $this->mOptionsLoaded || !$this->getId() )
wfRunHooks( 'UserLoadOptions', array( $this, &$this->mOptions ) );
}
+ /**
+ * @todo document
+ */
protected function saveOptions() {
global $wgAllowPrefChange;
// Allow hooks to abort, for instance to save to a global profile.
// Reset options to default state before saving.
- if( !wfRunHooks( 'UserSaveOptions', array( $this, &$saveOptions ) ) )
+ if( !wfRunHooks( 'UserSaveOptions', array( $this, &$saveOptions ) ) ) {
return;
+ }
foreach( $saveOptions as $key => $value ) {
# Don't bother storing default values
}
}
- $dbw->begin();
$dbw->delete( 'user_properties', array( 'up_user' => $this->getId() ), __METHOD__ );
$dbw->insert( 'user_properties', $insert_rows, __METHOD__ );
- $dbw->commit();
}
/**