'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;
+ $mEmailToken, $mEmailTokenExpires, $mRegistration, $mEditCount,
+ $mGroups, $mOptionOverrides;
//@}
/**
*/
var $mBlock;
+ /**
+ * @var bool
+ */
+ var $mAllowUsertalk;
+
/**
* @var Block
*/
* 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|false The corresponding username
+ * @return String|bool The corresponding username
*/
public static function whoIs( $id ) {
$dbr = wfGetDB( DB_SLAVE );
* Get the real name of a user given their user ID
*
* @param $id Int User ID
- * @return String|false 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 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 );
}
}
/**
- * Return a random password. Sourced from mt_rand, so it's not particularly secure.
- * @todo hash random numbers to improve security, like generateToken()
+ * Return a random password.
*
* @return String new random password
*/
public static function randomPassword() {
global $wgMinimalPasswordLength;
- $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
- $l = strlen( $pwchars ) - 1;
-
- $pwlength = max( 7, $wgMinimalPasswordLength );
- $digit = mt_rand( 0, $pwlength - 1 );
- $np = '';
- for ( $i = 0; $i < $pwlength; $i++ ) {
- $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars[ mt_rand( 0, $l ) ];
- }
- return $np;
+ // Decide the final password length based on our min password length, stopping at a minimum of 10 chars
+ $length = max( 10, $wgMinimalPasswordLength );
+ // Multiply by 1.25 to get the number of hex characters we need
+ $length = $length * 1.25;
+ // Generate random hex chars
+ $hex = MWCryptRand::generateHex( $length );
+ // Convert from base 16 to base 32 to get a proper password like string
+ return wfBaseConvert( $hex, 16, 32 );
}
/**
$this->mTouched = '0'; # Allow any pages to be cached
}
- $this->setToken(); # Random
+ $this->mToken = null; // Don't run cryptographic functions till we need a token
$this->mEmailAuthenticated = null;
$this->mEmailToken = '';
$this->mEmailTokenExpires = null;
return false;
}
- if ( $request->getSessionData( 'wsToken' ) !== null ) {
- $passwordCorrect = $proposedUser->getToken() === $request->getSessionData( 'wsToken' );
+ if ( $request->getSessionData( 'wsToken' ) ) {
+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' );
$from = 'session';
- } elseif ( $request->getCookie( 'Token' ) !== null ) {
- $passwordCorrect = $proposedUser->getToken() === $request->getCookie( 'Token' );
+ } elseif ( $request->getCookie( 'Token' ) ) {
+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getCookie( 'Token' );
$from = 'cookie';
} else {
# No session or persistent login cookie
}
$this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
$this->mToken = $row->user_token;
+ if ( $this->mToken == '' ) {
+ $this->mToken = null;
+ }
$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 );
}
$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;
// overwriting mBlockedby, surely?
$this->load();
- $this->mBlockedby = 0;
- $this->mHideName = 0;
- $this->mAllowUsertalk = 0;
-
# We only need to worry about passing the IP address to the Block generator if the
# user is not immune to autoblocks/hardblocks, and they are the current user so we
# know which IP address they're actually coming from
}
# User/IP blocking
- $this->mBlock = Block::newFromTarget( $this->getName(), $ip, !$bFromSlave );
- if ( $this->mBlock instanceof Block ) {
- wfDebug( __METHOD__ . ": Found block.\n" );
- $this->mBlockedby = $this->mBlock->getByName();
- $this->mBlockreason = $this->mBlock->mReason;
- $this->mHideName = $this->mBlock->mHideName;
- $this->mAllowUsertalk = !$this->mBlock->prevents( 'editownusertalk' );
- }
+ $block = Block::newFromTarget( $this->getName(), $ip, !$bFromSlave );
# Proxy blocking
- if ( $ip !== null && !$this->isAllowed( 'proxyunbannable' ) && !in_array( $ip, $wgProxyWhitelist ) ) {
+ if ( !$block instanceof Block && $ip !== null && !$this->isAllowed( 'proxyunbannable' )
+ && !in_array( $ip, $wgProxyWhitelist ) )
+ {
# Local list
if ( self::isLocallyBlockedProxy( $ip ) ) {
- $this->mBlockedby = wfMsg( 'proxyblocker' );
- $this->mBlockreason = wfMsg( 'proxyblockreason' );
+ $block = new Block;
+ $block->setBlocker( wfMsg( 'proxyblocker' ) );
+ $block->mReason = wfMsg( 'proxyblockreason' );
+ $block->setTarget( $ip );
+ } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
+ $block = new Block;
+ $block->setBlocker( wfMsg( 'sorbs' ) );
+ $block->mReason = wfMsg( 'sorbsreason' );
+ $block->setTarget( $ip );
}
+ }
- # DNSBL
- if ( !$this->mBlockedby && !$this->getID() ) {
- if ( $this->isDnsBlacklisted( $ip ) ) {
- $this->mBlockedby = wfMsg( 'sorbs' );
- $this->mBlockreason = wfMsg( 'sorbsreason' );
- }
- }
+ if ( $block instanceof Block ) {
+ wfDebug( __METHOD__ . ": Found block.\n" );
+ $this->mBlock = $block;
+ $this->mBlockedby = $block->getByName();
+ $this->mBlockreason = $block->mReason;
+ $this->mHideName = $block->mHideName;
+ $this->mAllowUsertalk = !$block->prevents( 'editownusertalk' );
+ } else {
+ $this->mBlockedby = '';
+ $this->mHideName = 0;
+ $this->mAllowUsertalk = false;
}
# Extensions
$count = $wgMemc->get( $key );
// Already pinged?
if( $count ) {
- if( $count > $max ) {
+ if( $count >= $max ) {
wfDebug( __METHOD__ . ": tripped! $key at $count $summary\n" );
if( $wgRateLimitLog ) {
wfSuppressWarnings();
/**
* Get the user's current token.
+ * @param $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility)
* @return String Token
*/
- public function getToken() {
+ public function getToken( $forceCreation = true ) {
$this->load();
+ if ( !$this->mToken && $forceCreation ) {
+ $this->setToken();
+ }
return $this->mToken;
}
* @param $token String|bool If specified, set the token to this value
*/
public function setToken( $token = false ) {
- global $wgSecretKey, $wgProxyKey;
$this->load();
if ( !$token ) {
- if ( $wgSecretKey ) {
- $key = $wgSecretKey;
- } elseif ( $wgProxyKey ) {
- $key = $wgProxyKey;
- } else {
- $key = microtime();
- }
- $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId );
+ $this->mToken = MWCryptRand::generateHex( USER_TOKEN_LENGTH );
} else {
$this->mToken = $token;
}
}
- /**
- * 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 ) );
}
+ /**
+ * Set the user's e-mail address and a confirmation mail if needed.
+ *
+ * @since 1.20
+ * @param $str String New e-mail address
+ * @return Status
+ */
+ public function setEmailWithConfirmation( $str ) {
+ global $wgEnableEmail, $wgEmailAuthentication;
+
+ if ( !$wgEnableEmail ) {
+ return Status::newFatal( 'emaildisabled' );
+ }
+
+ $oldaddr = $this->getEmail();
+ if ( $str === $oldaddr ) {
+ return Status::newGood( true );
+ }
+
+ $this->setEmail( $str );
+
+ if ( $str !== '' && $wgEmailAuthentication ) {
+ # Send a confirmation request to the new address if needed
+ $type = $oldaddr != '' ? 'changed' : 'set';
+ $result = $this->sendConfirmationMail( $type );
+ if ( $result->isGood() ) {
+ # Say the the caller that a confirmation mail has been sent
+ $result->value = 'eauth';
+ }
+ } else {
+ $result = Status::newGood( true );
+ }
+
+ return $result;
+ }
+
/**
* Get the user's real name
* @return String User's real name
* Reset all options to the site defaults
*/
public function resetOptions() {
+ $this->load();
+
$this->mOptions = self::getDefaultOptions();
+ $this->mOptionsLoaded = true;
}
/**
/**
* 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] );
- }
- if ( is_null( $ns ) ) {
- return $this->mRights[$key];
- } else {
- // Merge non namespace specific rights
- return array_merge( $this->mRights[$key], $this->getRights() );
+ $this->mRights = array_values( $this->mRights );
}
-
+ return $this->mRights;
}
/**
}
$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();
}
$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
// 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 );
}
/**
* @deprecated in 1.19 due to removal of user_options from the user table
*/
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_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_touched' => $dbw->timestamp( $this->mTouched ),
- 'user_token' => $this->mToken,
+ 'user_token' => strval( $this->mToken ),
'user_email_token' => $this->mEmailToken,
'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
), array( /* WHERE */
'user_email' => $user->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
'user_real_name' => $user->mRealName,
- 'user_token' => $user->mToken,
+ 'user_token' => strval( $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_token' => $this->mToken,
+ 'user_token' => strval( $this->mToken ),
'user_registration' => $dbw->timestamp( $this->mRegistration ),
'user_editcount' => 0,
+ 'user_touched' => $dbw->timestamp( $this->mTouched ),
), __METHOD__
);
$this->mId = $dbw->insertId();
* @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
/**
* Alias for getEditToken.
- * @deprecated since 1.19, use getEditToken instead.
- *
+ * @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
} else {
$token = $request->getSessionData( 'wsEditToken' );
if ( $token === null ) {
- $token = self::generateToken();
+ $token = MWCryptRand::generateHex( 32 );
$request->setSessionData( 'wsEditToken', $token );
}
if( is_array( $salt ) ) {
*
* @param $salt String Optional salt value
* @return String The new random token
+ * @deprecated since 1.20; Use MWCryptRand for secure purposes or wfRandomString for pesudo-randomness
*/
public static function generateToken( $salt = '' ) {
- $token = dechex( mt_rand() ) . dechex( mt_rand() );
- return md5( $token . $salt );
+ return MWCryptRand::generateHex( 32 );
}
/**
* @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 );
}
$now = time();
$expires = $now + $wgUserEmailConfirmationTokenExpiry;
$expiration = wfTimestamp( TS_MW, $expires );
- $token = self::generateToken( $this->mId . $this->mEmail . $expires );
- $hash = md5( $token );
$this->load();
+ $token = MWCryptRand::generateHex( 32 );
+ $hash = md5( $token );
$this->mEmailToken = $hash;
$this->mEmailTokenExpires = $expiration;
return $token;
*
* @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( array $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;
}
}
if( $wgPasswordSalt ) {
if ( $salt === false ) {
- $salt = substr( wfGenerateToken(), 0, 8 );
+ $salt = MWCryptRand::generateHex( 8 );
}
return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
} else {
} elseif ( $type == ':B:' ) {
# Salted
list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
- return md5( $salt.'-'.md5( $password ) ) == $realHash;
+ return md5( $salt.'-'.md5( $password ) ) === $realHash;
} else {
# Old-style
return self::oldCrypt( $password, $userId ) === $hash;
}
/**
- * 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
* 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;
return true; // disabled
}
$log = new LogPage( 'newusers', false );
- $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) );
+ $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ), $this );
return true;
}
__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();
}
/**