'watchlisthideminor',
'ccmeonemails',
'diffonly',
+ 'showhiddencats',
);
/**
return false;
}
- # Save to cache
- $data = array();
- foreach ( self::$mCacheVars as $name ) {
- $data[$name] = $this->$name;
- }
- $data['mVersion'] = MW_USER_VERSION;
- $wgMemc->set( $key, $data );
+ $this->saveToCache();
} else {
wfDebug( "Got user {$this->mId} from cache\n" );
# Restore from cache
return true;
}
+ /**
+ * Save user data to the shared cache
+ */
+ function saveToCache() {
+ $this->load();
+ if ( $this->isAnon() ) {
+ // Anonymous users are uncached
+ return;
+ }
+ $data = array();
+ foreach ( self::$mCacheVars as $name ) {
+ $data[$name] = $this->$name;
+ }
+ $data['mVersion'] = MW_USER_VERSION;
+ $key = wfMemcKey( 'user', 'id', $this->mId );
+ global $wgMemc;
+ $wgMemc->set( $key, $data );
+ }
+
/**
* Static factory method for creation from username.
*
|| User::isIP( $name )
|| strpos( $name, '/' ) !== false
|| strlen( $name ) > $wgMaxNameChars
- || $name != $wgContLang->ucfirst( $name ) )
+ || $name != $wgContLang->ucfirst( $name ) ) {
+ wfDebugLog( 'username', __METHOD__ .
+ ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
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 );
if( is_null( $parsed )
|| $parsed->getNamespace()
- || strcmp( $name, $parsed->getPrefixedText() ) )
+ || strcmp( $name, $parsed->getPrefixedText() ) ) {
+ wfDebugLog( 'username', __METHOD__ .
+ ": '$name' invalid due to ambiguous prefixes" );
return false;
+ }
// Check an additional blacklist of troublemaker characters.
// Should these be merged into the title char list?
'\x{e000}-\x{f8ff}' . # private use
']/u';
if( preg_match( $unicodeBlacklist, $name ) ) {
+ wfDebugLog( 'username', __METHOD__ .
+ ": '$name' invalid due to blacklisted characters" );
return false;
}
* @return bool
*/
public static function isValidEmailAddr( $addr ) {
+ $result = null;
+ if( !wfRunHooks( 'isValidEmailAddr', array( $addr, &$result ) ) ) {
+ return $result;
+ }
+
return strpos( $addr, '@' ) !== false;
}
);
if( $field === null ) { // it has not been initialized. do so.
- $dbw = wfGetDb( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$count = $dbr->selectField(
'revision', 'count(*)',
array( 'rev_user' => $uid ),
* @deprecated use wfSetupSession()
*/
function SetupSession() {
+ wfDeprecated( __METHOD__ );
wfSetupSession();
}
return $result;
}
- global $wgRateLimits, $wgRateLimitsExcludedGroups;
+ global $wgRateLimits;
if( !isset( $wgRateLimits[$action] ) ) {
return false;
}
* @deprecated use User::newFromId()
*/
function setID( $v ) {
+ wfDeprecated( __METHOD__ );
$this->mId = $v;
$this->clearInstanceCache( 'id' );
}
global $wgMemc;
$key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
$newtalk = $wgMemc->get( $key );
- if( $newtalk != "" ) {
+ if( strval( $newtalk ) !== '' ) {
$this->mNewtalk = (bool)$newtalk;
} else {
- $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName() );
- $wgMemc->set( $key, (int)$this->mNewtalk, time() + 1800 );
+ // Since we are caching this, make sure it is up to date by getting it
+ // from the master
+ $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName(), true );
+ $wgMemc->set( $key, (int)$this->mNewtalk, 1800 );
}
} else {
$this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
/**
- * Perform a user_newtalk check on current slaves; if the memcached data
- * is funky we don't want newtalk state to get stuck on save, as that's
- * damn annoying.
- *
+ * Perform a user_newtalk check, uncached.
+ * Use getNewtalk for a cached check.
+ *
* @param string $field
* @param mixed $id
+ * @param bool $fromMaster True to fetch from the master, false for a slave
* @return bool
* @private
*/
- function checkNewtalk( $field, $id ) {
- $dbr = wfGetDB( DB_SLAVE );
- $ok = $dbr->selectField( 'user_newtalk', $field,
+ function checkNewtalk( $field, $id, $fromMaster = false ) {
+ if ( $fromMaster ) {
+ $db = wfGetDB( DB_MASTER );
+ } else {
+ $db = wfGetDB( DB_SLAVE );
+ }
+ $ok = $db->selectField( 'user_newtalk', $field,
array( $field => $id ), __METHOD__ );
return $ok !== false;
}
* @private
*/
function updateNewtalk( $field, $id ) {
- if( $this->checkNewtalk( $field, $id ) ) {
- wfDebug( __METHOD__." already set ($field, $id), ignoring\n" );
- return false;
- }
$dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'user_newtalk',
array( $field => $id ),
__METHOD__,
'IGNORE' );
- wfDebug( __METHOD__.": set on ($field, $id)\n" );
- return true;
+ if ( $dbw->affectedRows() ) {
+ wfDebug( __METHOD__.": set on ($field, $id)\n" );
+ return true;
+ } else {
+ wfDebug( __METHOD__." already set ($field, $id)\n" );
+ return false;
+ }
}
/**
* @private
*/
function deleteNewtalk( $field, $id ) {
- if( !$this->checkNewtalk( $field, $id ) ) {
- wfDebug( __METHOD__.": already gone ($field, $id), ignoring\n" );
- return false;
- }
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user_newtalk',
array( $field => $id ),
__METHOD__ );
- wfDebug( __METHOD__.": killed on ($field, $id)\n" );
- return true;
+ if ( $dbw->affectedRows() ) {
+ wfDebug( __METHOD__.": killed on ($field, $id)\n" );
+ return true;
+ } else {
+ wfDebug( __METHOD__.": already gone ($field, $id)\n" );
+ return false;
+ }
}
/**
$field = 'user_id';
$id = $this->getId();
}
+ global $wgMemc;
if( $val ) {
$changed = $this->updateNewtalk( $field, $id );
$changed = $this->deleteNewtalk( $field, $id );
}
- if( $changed ) {
- if( $this->isAnon() ) {
- // Anons have a separate memcached space, since
- // user records aren't kept for them.
- global $wgMemc;
- $key = wfMemcKey( 'newtalk', 'ip', $val );
- $wgMemc->set( $key, $val ? 1 : 0 );
- } else {
- if( $val ) {
- // Make sure the user page is watched, so a notification
- // will be sent out if enabled.
- $this->addWatch( $this->getTalkPage() );
- }
- }
+ if( $this->isAnon() ) {
+ // Anons have a separate memcached space, since
+ // user records aren't kept for them.
+ $key = wfMemcKey( 'newtalk', 'ip', $id );
+ $wgMemc->set( $key, $val ? 1 : 0, 1800 );
+ }
+ if ( $changed ) {
$this->invalidateCache();
}
}
/**
* Get the list of implicit group memberships this user has.
- * This includes all explicit groups, plus 'user' if logged in
- * and '*' for all accounts.
+ * This includes all explicit groups, plus 'user' if logged in,
+ * '*' for all accounts and autopromoted groups
* @param boolean $recache Don't use the cache
* @return array of strings
*/
$this->mEffectiveGroups[] = '*';
if( $this->mId ) {
$this->mEffectiveGroups[] = 'user';
-
- global $wgAutoConfirmAge, $wgAutoConfirmCount;
- $accountAge = time() - wfTimestampOrNull( TS_UNIX, $this->mRegistration );
- if( $accountAge >= $wgAutoConfirmAge && $this->getEditCount() >= $wgAutoConfirmCount ) {
- $this->mEffectiveGroups[] = 'autoconfirmed';
- }
- # Implicit group for users whose email addresses are confirmed
- global $wgEmailAuthentication;
- if( self::isValidEmailAddr( $this->mEmail ) ) {
- if( $wgEmailAuthentication ) {
- if( $this->mEmailAuthenticated )
- $this->mEffectiveGroups[] = 'emailconfirmed';
- } else {
- $this->mEffectiveGroups[] = 'emailconfirmed';
- }
- }
+ $this->mEffectiveGroups = array_unique( array_merge(
+ $this->mEffectiveGroups,
+ Autopromote::getAutopromoteGroups( $this )
+ ) );
+
# Hook for additional groups
wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
}
* @return bool
*/
function isLoggedIn() {
- if( $this->mId === null and $this->mName !== null ) {
- // Special-case optimization
- return !self::isIP( $this->mName );
- }
return $this->getID() != 0;
}
* @deprecated
*/
function isBot() {
+ wfDeprecated( __METHOD__ );
return $this->isAllowed( 'bot' );
}
'wl_notificationtimestamp' => NULL
), array( /* WHERE */
'wl_user' => $currentUser
- ), 'UserMailer::clearAll'
+ ), __METHOD__
);
# we also need to clear here the "you have new message" notification for the own user_talk page
}
/**
- * Logout user
- * Clears the cookies and session, resets the instance cache
+ * Logout user.
*/
function logout() {
+ global $wgUser;
+ if( wfRunHooks( 'UserLogout', array(&$this) ) ) {
+ $this->doLogout();
+ wfRunHooks( 'UserLogoutComplete', array(&$wgUser) );
+ }
+ }
+
+ /**
+ * Really logout user
+ * Clears the cookies and session, resets the instance cache
+ */
+ function doLogout() {
global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix;
$this->clearInstanceCache( 'defaults' );
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_options' => $this->encodeOptions(),
'user_touched' => $dbw->timestamp($this->mTouched),
- 'user_token' => $this->mToken
+ 'user_token' => $this->mToken,
+ 'user_email_token' => $this->mEmailToken,
+ 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
), array( /* WHERE */
'user_id' => $this->mId
), __METHOD__
/**
- * Checks if a user with the given name exists, returns the ID
+ * Checks if a user with the given name exists, returns the ID.
*/
function idForName() {
$s = trim( $this->getName() );
- if ( 0 == strcmp( '', $s ) ) return 0;
+ if ( $s === '' ) return 0;
$dbr = wfGetDB( DB_SLAVE );
$id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
/**
* @deprecated
*/
- function setLoaded( $loaded ) {}
+ function setLoaded( $loaded ) {
+ wfDeprecated( __METHOD__ );
+ }
/**
* Get this user's personal page title.
} elseif( $wgAuth->strict() ) {
/* Auth plugin doesn't allow local authentication */
return false;
+ } elseif( $wgAuth->strictUserAuth( $this->getName() ) ) {
+ /* Auth plugin doesn't allow local authentication for this user name */
+ return false;
}
$ep = $this->encryptPassword( $password );
if ( 0 == strcmp( $ep, $this->mPassword ) ) {
}
/**
- * Generate a new e-mail confirmation token and send a confirmation
+ * Generate a new e-mail confirmation token and send a confirmation/invalidation
* mail to the user's given address.
*
* @return mixed True on success, a WikiError object on failure.
*/
function sendConfirmationMail() {
- global $wgContLang;
+ global $wgLang;
$expiration = null; // gets passed-by-ref and defined in next line.
- $url = $this->confirmationTokenUrl( $expiration );
+ $token = $this->confirmationToken( $expiration );
+ $url = $this->confirmationTokenUrl( $token );
+ $invalidateURL = $this->invalidationTokenUrl( $token );
return $this->sendMail( wfMsg( 'confirmemail_subject' ),
wfMsg( 'confirmemail_body',
wfGetIP(),
$this->getName(),
$url,
- $wgContLang->timeanddate( $expiration, false ) ) );
+ $wgLang->timeanddate( $expiration, false ),
+ $invalidateURL ) );
}
/**
*
* @param string $subject
* @param string $body
- * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
+ * @param string $from Optional from address; default $wgPasswordSender will be used otherwise.
* @return mixed True on success, a WikiError object on failure.
*/
- function sendMail( $subject, $body, $from = null ) {
+ function sendMail( $subject, $body, $from = null, $replyto = null ) {
if( is_null( $from ) ) {
global $wgPasswordSender;
$from = $wgPasswordSender;
}
- require_once( 'UserMailer.php' );
$to = new MailAddress( $this );
$sender = new MailAddress( $from );
- $error = userMailer( $to, $sender, $subject, $body );
-
- if( $error == '' ) {
- return true;
- } else {
- return new WikiError( $error );
- }
+ return UserMailer::send( $to, $sender, $subject, $body, $replyto );
}
/**
$now = time();
$expires = $now + 7 * 24 * 60 * 60;
$expiration = wfTimestamp( TS_MW, $expires );
-
$token = $this->generateToken( $this->mId . $this->mEmail . $expires );
$hash = md5( $token );
-
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update( 'user',
- array( 'user_email_token' => $hash,
- 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
- array( 'user_id' => $this->mId ),
- __METHOD__ );
-
+ $this->load();
+ $this->mEmailToken = $hash;
+ $this->mEmailTokenExpires = $expiration;
+ $this->saveSettings();
return $token;
}
/**
- * Generate and store a new e-mail confirmation token, and return
- * the URL the user can use to confirm.
- * @param &$expiration mixed output: accepts the expiration time
+ * Return a URL the user can use to confirm their email address.
+ * @param $token: accepts the email confirmation token
* @return string
* @private
*/
- function confirmationTokenUrl( &$expiration ) {
- $token = $this->confirmationToken( $expiration );
+ function confirmationTokenUrl( $token ) {
$title = SpecialPage::getTitleFor( 'Confirmemail', $token );
return $title->getFullUrl();
}
+ /**
+ * Return a URL the user can use to invalidate their email address.
+ * @param $token: accepts the email confirmation token
+ * @return string
+ * @private
+ */
+ function invalidationTokenUrl( $token ) {
+ $title = SpecialPage::getTitleFor( 'Invalidateemail', $token );
+ return $title->getFullUrl();
+ }
/**
* Mark the e-mail address confirmed and save.
$this->saveSettings();
return true;
}
+
+ /**
+ * Invalidate the user's email confirmation, unauthenticate the email
+ * if it was already confirmed and save.
+ */
+ function invalidateEmail() {
+ $this->load();
+ $this->mEmailToken = null;
+ $this->mEmailTokenExpires = null;
+ $this->mEmailAuthenticated = null;
+ $this->saveSettings();
+ return true;
+ }
/**
* Is this user allowed to send e-mails within limits of current
* @return bool
*/
function canSendEmail() {
- return $this->isEmailConfirmed();
+ $canSend = $this->isEmailConfirmed();
+ wfRunHooks( 'UserCanSendEmail', array( &$this, &$canSend ) );
+ return $canSend;
}
/**
* @return bool
*/
function canReceiveEmail() {
- return $this->canSendEmail() && !$this->getOption( 'disablemail' );
+ return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
}
/**
* @return array
*/
public static function getImplicitGroups() {
- static $groups = null;
- if( !is_array( $groups ) ) {
- $groups = array( '*', 'user', 'autoconfirmed', 'emailconfirmed' );
- wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) );
- }
+ global $wgImplicitGroups;
+ $groups = $wgImplicitGroups;
+ wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) ); #deprecated, use $wgImplictGroups instead
return $groups;
}
}
+