'deletedhistory',
'deletedtext',
'deleterevision',
- 'disableaccount',
'edit',
'editinterface',
'editusercssjs', #deprecated
'reupload',
'reupload-shared',
'rollback',
- 'selenium',
'sendemail',
'siteadmin',
'suppressionlog',
'suppressredirect',
'suppressrevision',
- 'trackback',
'unblockself',
'undelete',
'unwatchedpages',
var $mId, $mName, $mRealName, $mPassword, $mNewpassword, $mNewpassTime,
$mEmail, $mTouched, $mToken, $mEmailAuthenticated,
$mEmailToken, $mEmailTokenExpires, $mRegistration, $mGroups, $mOptionOverrides,
- $mCookiePassword, $mEditCount, $mAllowUsertalk;
+ $mEditCount, $mAllowUsertalk;
//@}
/**
* If the code is invalid or has expired, returns NULL.
*
* @param $code String Confirmation code
- * @return User
+ * @return User object, or null
*/
public static function newFromConfirmationCode( $code ) {
$dbr = wfGetDB( DB_SLAVE );
*
* @param $request WebRequest object to use; $wgRequest will be used if
* ommited.
- * @return User
+ * @return User object
*/
public static function newFromSession( WebRequest $request = null ) {
$user = new User;
/**
* Get the username corresponding to a given user ID
* @param $id Int User ID
- * @return String The corresponding username
+ * @return String|bool 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|bool The corresponding user's real name
*/
public static function whoIsReal( $id ) {
$dbr = wfGetDB( DB_SLAVE );
return null;
}
+ if ( User::isIP( $name ) ) {
+ # Cannot exist
+ return null;
+ }
+
if ( isset( self::$idCacheByName[$name] ) ) {
return self::$idCacheByName[$name];
}
return false;
}
+
// Ensure that the name can't be misresolved as a different title,
// such as with extra namespace keys at the start.
$parsed = Title::newFromText( $name );
* @deprecated since 1.18 call Sanitizer::isValidEmail() directly
*/
public static function isValidEmailAddr( $addr ) {
+ wfDeprecated( __METHOD__, '1.18' );
return Sanitizer::validateEmail( $addr );
}
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 ) )
);
}
}
$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
*/
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 ) {
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;
}
}
}
- /**
- * Set the cookie password
- *
- * @param $str String New cookie password
- */
- private function setCookiePassword( $str ) {
- $this->load();
- $this->mCookiePassword = md5( $str );
- }
-
/**
* Set the password for a password reminder or new account email
*
*/
public function setEmail( $str ) {
$this->load();
+ if( $str == $this->mEmail ) {
+ return;
+ }
$this->mEmail = $str;
+ $this->invalidateEmail();
wfRunHooks( 'UserSetEmail', array( $this, &$this->mEmail ) );
}
/**
* Get the permissions this user has.
- * @param $ns int If numeric, get permissions for this namespace
* @return Array of String permission names
*/
- public function getRights( $ns = null ) {
- $key = is_null( $ns ) ? '*' : intval( $ns );
-
+ public function getRights() {
if ( is_null( $this->mRights ) ) {
- $this->mRights = array();
- }
-
- if ( !isset( $this->mRights[$key] ) ) {
- $this->mRights[$key] = self::getGroupPermissions( $this->getEffectiveGroups(), $ns );
- wfRunHooks( 'UserGetRights', array( $this, &$this->mRights[$key], $ns ) );
+ $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
+ wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
// Force reindexation of rights when a hook has unset one of them
- $this->mRights[$key] = array_values( $this->mRights[$key] );
+ $this->mRights = array_values( $this->mRights );
}
- if ( is_null( $ns ) ) {
- return $this->mRights[$key];
- } else {
- // Merge non namespace specific rights
- return array_merge( $this->mRights[$key], $this->getRights() );
- }
-
+ return $this->mRights;
}
/**
*/
public function getGroups() {
$this->load();
+ $this->loadGroups();
return $this->mGroups;
}
}
$this->loadGroups();
$this->mGroups[] = $group;
- $this->mRights = null;
+ $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
$this->invalidateCache();
}
}
$this->loadGroups();
$this->mGroups = array_diff( $this->mGroups, array( $group ) );
- $this->mRights = null;
+ $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
$this->invalidateCache();
}
/**
* Internal mechanics of testing a permission
* @param $action String
- * @param $ns int|null Namespace optional
* @return bool
*/
- public function isAllowed( $action = '', $ns = null ) {
+ public function isAllowed( $action = '' ) {
if ( $action === '' ) {
return true; // In the spirit of DWIM
}
}
# Use strict parameter to avoid matching numeric 0 accidentally inserted
# by misconfiguration: 0 == 'foo'
- return in_array( $action, $this->getRights( $ns ), true );
+ return in_array( $action, $this->getRights(), true );
}
/**
* @deprecated since 1.18 Use ->getSkin() in the most relevant outputting context you have
*/
public function getSkin() {
+ wfDeprecated( __METHOD__, '1.18' );
return RequestContext::getMain()->getSkin();
}
// The query to find out if it is watched is cached both in memcached and per-invocation,
// and when it does have to be executed, it can be on a slave
// If this is the user's newtalk page, we always update the timestamp
- if( $title->getNamespace() == NS_USER_TALK &&
+ $force = '';
+ if ( $title->getNamespace() == NS_USER_TALK &&
$title->getText() == $this->getName() )
{
- $watched = true;
- } else {
- $watched = $this->isWatched( $title );
+ $force = 'force';
}
- // If the page is watched by the user (or may be watched), update the timestamp on any
- // any matching rows
- if ( $watched ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update( 'watchlist',
- array( /* SET */
- 'wl_notificationtimestamp' => null
- ), array( /* WHERE */
- 'wl_title' => $title->getDBkey(),
- 'wl_namespace' => $title->getNamespace(),
- 'wl_user' => $this->getID()
- ), __METHOD__
- );
- }
+ $wi = WatchedItem::fromUserTitle( $this, $title );
+ $wi->resetNotificationTimestamp( $force );
}
/**
/**
* 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
*/
- public function decodeOptions( $str ) {
+ private function decodeOptions( $str ) {
+ wfDeprecated( __METHOD__, '1.19' );
if( !$str )
return;
$this->load();
if ( 0 == $this->mId ) return;
+ if ( !$this->mToken ) {
+ // When token is empty or NULL generate a new one and then save it to the database
+ // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey
+ // Simply by setting every cell in the user_token column to NULL and letting them be
+ // regenerated as users log back into the wiki.
+ $this->setToken();
+ $this->saveSettings();
+ }
$session = array(
'wsUserID' => $this->mId,
'wsToken' => $this->mToken,
'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_touched' => $dbw->timestamp( self::newTouchedTimestamp() ),
);
foreach ( $params as $name => $value ) {
$fields["user_$name"] = $value;
*/
public function addToDatabase() {
$this->load();
+
+ $this->mTouched = self::newTouchedTimestamp();
+
$dbw = wfGetDB( DB_MASTER );
$seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
$dbw->insert( 'user',
'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,
+ 'user_touched' => $dbw->timestamp( $this->mTouched ),
), __METHOD__
);
$this->mId = $dbw->insertId();
}
/**
- * 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() );
}
/**
* @return String Page rendering hash
*/
public function getPageRenderingHash() {
+ wfDeprecated( __METHOD__, '1.17' );
+
global $wgUseDynamicDates, $wgRenderHashAppend, $wgLang, $wgContLang;
if( $this->mHash ){
return $this->mHash;
}
- wfDeprecated( __METHOD__ );
// stubthreshold is only included below for completeness,
// since it disables the parser cache, its value will always
# 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
}
}
+ /**
+ * 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 ) {
+ wfDeprecated( __METHOD__, '1.19' );
+ 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 ),
/**
* 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 ),
- PROT_HTTP
- );
+ // Hack to bypass localization of 'Special:'
+ $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
+ return $title->getCanonicalUrl();
}
/**
*
* @note Call saveSettings() after calling this function to commit the change.
*
- * @return true
+ * @return bool
*/
public function confirmEmail() {
$this->setEmailAuthenticationTimestamp( wfTimestampNow() );
* address if it was already confirmed.
*
* @note Call saveSettings() after calling this function to commit the change.
- * @return true
+ * @return bool Returns true
*/
function invalidateEmail() {
$this->load();
* 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( $groups ) {
global $wgGroupPermissions, $wgRevokePermissions;
$rights = array();
-
- // Grant every granted permission first
+ // grant every granted permission first
foreach( $groups as $group ) {
if( isset( $wgGroupPermissions[$group] ) ) {
- $rights = array_merge( $rights, self::extractRights(
- $wgGroupPermissions[$group], $ns ) );
+ $rights = array_merge( $rights,
+ // array_filter removes empty items
+ array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
}
}
-
- // Revoke the revoked permissions
+ // now revoke the revoked permissions
foreach( $groups as $group ) {
if( isset( $wgRevokePermissions[$group] ) ) {
- $rights = array_diff( $rights, self::extractRights(
- $wgRevokePermissions[$group], $ns ) );
+ $rights = array_diff( $rights,
+ array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
}
}
return array_unique( $rights );
}
- /**
- * Helper for User::getGroupPermissions
- * @param $list array
- * @param $ns int
- * @return array
- */
- private static function extractRights( $list, $ns ) {
- $rights = array();
- foreach( $list as $right => $value ) {
- if ( is_array( $value ) ) {
- # This is a list of namespaces where the permission applies
- if ( !is_null( $ns ) && !empty( $value[$ns] ) ) {
- $rights[] = $right;
- }
- } else {
- # This is a boolean indicating that the permission applies
- if ( $value ) {
- $rights[] = $right;
- }
- }
- }
- return $rights;
- }
-
/**
* 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 ) {
+ public static function getGroupsWithPermission( $role ) {
global $wgGroupPermissions;
$allowedGroups = array();
foreach ( $wgGroupPermissions as $group => $rights ) {
- if ( in_array( $role, self::getGroupPermissions( array( $group ), $ns ), true ) ) {
+ if ( isset( $rights[$role] ) && $rights[$role] ) {
$allowedGroups[] = $group;
}
}
* 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();
}
}
/**
- * Add a newuser log entry for this user
+ * Add a newuser log entry for this user. Before 1.19 the return value was always true.
*
* @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;
}
/**
* Add an autocreate newuser log entry for this user
* Used by things like CentralAuth and perhaps other authplugins.
*
- * @return true
+ * @return bool
*/
public function addNewUserLogEntryAutoCreate() {
global $wgNewUserLog;
__METHOD__
);
+ $this->mOptionOverrides = array();
foreach ( $res as $row ) {
$this->mOptionOverrides[$row->up_property] = $row->up_value;
$this->mOptions[$row->up_property] = $row->up_value;
}
}
- $dbw->begin();
$dbw->delete( 'user_properties', array( 'up_user' => $this->getId() ), __METHOD__ );
$dbw->insert( 'user_properties', $insert_rows, __METHOD__ );
- $dbw->commit();
}
/**