*/
class User {
- /**
- * \type{\arrayof{\string}} A list of default user toggles, i.e., boolean user
- * preferences that are displayed by Special:Preferences as checkboxes.
- * This list can be extended via the UserToggles hook or by
- * $wgContLang::getExtraUserToggles().
- * @showinitializer
- */
- public static $mToggles = array(
- 'highlightbroken',
- 'justify',
- 'hideminor',
- 'extendwatchlist',
- 'usenewrc',
- 'numberheadings',
- 'showtoolbar',
- 'editondblclick',
- 'editsection',
- 'editsectiononrightclick',
- 'showtoc',
- 'rememberpassword',
- 'editwidth',
- 'watchcreations',
- 'watchdefault',
- 'watchmoves',
- 'watchdeletion',
- 'minordefault',
- 'previewontop',
- 'previewonfirst',
- 'nocache',
- 'enotifwatchlistpages',
- 'enotifusertalkpages',
- 'enotifminoredits',
- 'enotifrevealaddr',
- 'shownumberswatching',
- 'fancysig',
- 'externaleditor',
- 'externaldiff',
- 'showjumplinks',
- 'uselivepreview',
- 'forceeditsummary',
- 'watchlisthideminor',
- 'watchlisthidebots',
- 'watchlisthideown',
- 'watchlisthideanons',
- 'watchlisthideliu',
- 'ccmeonemails',
- 'diffonly',
- 'showhiddencats',
- 'noconvertlink',
- 'norollbackdiff',
- );
-
/**
* \type{\arrayof{\string}} List of member variables which are saved to the
* shared cache (memcached). Any operation which changes the
'reupload',
'reupload-shared',
'rollback',
+ 'selenium',
'sendemail',
'siteadmin',
'suppressionlog',
* User::getCanonicalName(), except that true is accepted as an alias
* for 'valid', for BC.
*
- * @return \type{User} The User object, or null if the username is invalid. If the
+ * @return \type{User} The User object, or false if the username is invalid
+ * (e.g. if it contains illegal characters or is an IP address). If the
* username is not present in the database, the result will be a user object
* with a name, zero user ID and default settings.
*/
* either by batch processes or by user accounts which have
* already been created.
*
- * Additional character blacklisting may be added here
- * rather than in isValidUserName() to avoid disrupting
- * existing accounts.
+ * Additional blacklisting may be added here rather than in
+ * isValidUserName() to avoid disrupting existing accounts.
*
* @param $name \string String to match
* @return \bool True or false
*/
static function isCreatableName( $name ) {
global $wgInvalidUsernameCharacters;
- return
- self::isUsableName( $name ) &&
- // Registration-time character blacklisting...
- !preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name );
+ // Ensure that the username isn't longer than 235 bytes, so that
+ // (at least for the builtin skins) user javascript and css files
+ // will work. (bug 23080)
+ if( strlen( $name ) > 235 ) {
+ wfDebugLog( 'username', __METHOD__ .
+ ": '$name' invalid due to length" );
+ return false;
+ }
+
+ if( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) {
+ wfDebugLog( 'username', __METHOD__ .
+ ": '$name' invalid due to wgInvalidUsernameCharacters" );
+ return false;
+ }
+
+ return self::isUsableName( $name );
}
/**
function loadDefaults( $name = false ) {
wfProfileIn( __METHOD__ );
- global $wgCookiePrefix;
+ global $wgRequest;
$this->mId = 0;
$this->mName = $name;
$this->mOptionOverrides = null;
$this->mOptionsLoaded = false;
- if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) {
- $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgCookiePrefix.'LoggedOut'] );
+ if( $wgRequest->getCookie( 'LoggedOut' ) !== null ) {
+ $this->mTouched = wfTimestamp( TS_MW, $wgRequest->getCookie( 'LoggedOut' ) );
} else {
$this->mTouched = '0'; # Allow any pages to be cached
}
* @return \bool True if the user is logged in, false otherwise.
*/
private function loadFromSession() {
- global $wgMemc, $wgCookiePrefix, $wgExternalAuthType, $wgAutocreatePolicy;
+ global $wgRequest, $wgExternalAuthType, $wgAutocreatePolicy;
$result = null;
wfRunHooks( 'UserLoadFromSession', array( $this, &$result ) );
}
}
- if ( isset( $_COOKIE["{$wgCookiePrefix}UserID"] ) ) {
- $sId = intval( $_COOKIE["{$wgCookiePrefix}UserID"] );
+ if ( $wgRequest->getCookie( 'UserID' ) !== null ) {
+ $sId = intval( $wgRequest->getCookie( 'UserID' ) );
if( isset( $_SESSION['wsUserID'] ) && $sId != $_SESSION['wsUserID'] ) {
$this->loadDefaults(); // Possible collision!
wfDebugLog( 'loginSessions', "Session user ID ({$_SESSION['wsUserID']}) and
if ( isset( $_SESSION['wsUserName'] ) ) {
$sName = $_SESSION['wsUserName'];
- } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserName"] ) ) {
- $sName = $_COOKIE["{$wgCookiePrefix}UserName"];
+ } else if ( $wgRequest->getCookie('UserName') !== null ) {
+ $sName = $wgRequest->getCookie('UserName');
$_SESSION['wsUserName'] = $sName;
} else {
$this->loadDefaults();
if ( isset( $_SESSION['wsToken'] ) ) {
$passwordCorrect = $_SESSION['wsToken'] == $this->mToken;
$from = 'session';
- } else if ( isset( $_COOKIE["{$wgCookiePrefix}Token"] ) ) {
- $passwordCorrect = $this->mToken == $_COOKIE["{$wgCookiePrefix}Token"];
+ } else if ( $wgRequest->getCookie( 'Token' ) !== null ) {
+ $passwordCorrect = $this->mToken == $wgRequest->getCookie( 'Token' );
$from = 'cookie';
} else {
# No session or persistent login cookie
}
}
- /**
- * Get a list of user toggle names
- * @return \type{\arrayof{\string}} Array of user toggle names
- */
- static function getToggles() {
- global $wgContLang, $wgUseRCPatrol;
- $extraToggles = array();
- wfRunHooks( 'UserToggles', array( &$extraToggles ) );
- if( $wgUseRCPatrol ) {
- $extraToggles[] = 'hidepatrolled';
- $extraToggles[] = 'newpageshidepatrolled';
- $extraToggles[] = 'watchlisthidepatrolled';
- }
- return array_merge( self::$mToggles, $extraToggles, $wgContLang->getExtraUserToggles() );
- }
-
/**
* Get blocking information
if ( $this->mBlock->load( $ip , $this->mId ) ) {
wfDebug( __METHOD__ . ": Found block.\n" );
$this->mBlockedby = $this->mBlock->mBy;
- if( $this->mBlockedby == "0" )
+ if( $this->mBlockedby == 0 )
$this->mBlockedby = $this->mBlock->mByName;
$this->mBlockreason = $this->mBlock->mReason;
$this->mHideName = $this->mBlock->mHideName;
return $this->mDatePreference;
}
+ /**
+ * Get the user preferred stub threshold
+ */
+ function getStubThreshold() {
+ global $wgMaxArticleSize; # Maximum article size, in Kb
+ $threshold = intval( $this->getOption( 'stubthreshold' ) );
+ if ( $threshold > $wgMaxArticleSize * 1024 ) {
+ # If they have set an impossible value, disable the preference
+ # so we can use the parser cache again.
+ $threshold = 0;
+ }
+ return $threshold;
+ }
+
/**
* Get the permissions this user has.
* @return \type{\arrayof{\string}} Array of permission names
'ug_user' => $this->getID(),
'ug_group' => $group,
),
- 'User::addGroup',
+ __METHOD__,
array( 'IGNORE' ) );
}
array(
'ug_user' => $this->getID(),
'ug_group' => $group,
- ),
- 'User::removeGroup' );
+ ), __METHOD__ );
$this->loadGroups();
$this->mGroups = array_diff( $this->mGroups, array( $group ) );
$userSkin = $wgDefaultSkin;
}
- $this->mSkin =& Skin::newFromKey( $userSkin );
+ $this->mSkin = Skin::newFromKey( $userSkin );
wfProfileOut( __METHOD__ );
}
if( $t || !$this->mSkin->getTitle() ) {
}
// stubthreshold is only included below for completeness,
- // it will always be 0 when this function is called by parsercache.
+ // since it disables the parser cache, its value will always
+ // be 0 when this function is called by parsercache.
$confstr = $this->getOption( 'math' );
- $confstr .= '!' . $this->getOption( 'stubthreshold' );
- if ( $wgUseDynamicDates ) {
+ $confstr .= '!' . $this->getStubThreshold();
+ if ( $wgUseDynamicDates ) { # This is wrong (bug 24714)
$confstr .= '!' . $this->getDatePreference();
}
$confstr .= '!' . ( $this->getOption( 'numberheadings' ) ? '1' : '' );
$extra = $wgContLang->getExtraHashOptions();
$confstr .= $extra;
+ // Since the skin could be overloading link(), it should be
+ // included here but in practice, none of our skins do that.
+
$confstr .= $wgRenderHashAppend;
// Give a chance for extensions to modify the hash, if they have
return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
}
- /**
- * @deprecated
- */
- function setLoaded( $loaded ) {
- wfDeprecated( __METHOD__ );
- }
-
/**
* Get this user's personal page title.
*
return $res;
else {
$dbr = wfGetDB( DB_SLAVE );
- return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
+ return $res = $dbr->selectField( 'user', 'max(user_id)', false, __METHOD__ );
}
}
return EDIT_TOKEN_SUFFIX;
} else {
if( !isset( $_SESSION['wsEditToken'] ) ) {
- $token = $this->generateToken();
+ $token = self::generateToken();
$_SESSION['wsEditToken'] = $token;
} else {
$token = $_SESSION['wsEditToken'];
* @param $salt \string Optional salt value
* @return \string The new random token
*/
- function generateToken( $salt = '' ) {
+ public static function generateToken( $salt = '' ) {
$token = dechex( mt_rand() ) . dechex( mt_rand() );
return md5( $token . $salt );
}
* Generate a new e-mail confirmation token and send a confirmation/invalidation
* mail to the user's given address.
*
+ * @param $changed Boolean: whether the adress changed
* @return \types{\bool,\type{WikiError}} True on success, a WikiError object on failure.
*/
- function sendConfirmationMail() {
+ function sendConfirmationMail( $changed = false ) {
global $wgLang;
$expiration = null; // gets passed-by-ref and defined in next line.
$token = $this->confirmationToken( $expiration );
$invalidateURL = $this->invalidationTokenUrl( $token );
$this->saveSettings();
+ $message = $changed ? 'confirmemail_body_changed' : 'confirmemail_body';
return $this->sendMail( wfMsg( 'confirmemail_subject' ),
- wfMsg( 'confirmemail_body',
+ wfMsg( $message,
wfGetIP(),
$this->getName(),
$url,
$now = time();
$expires = $now + 7 * 24 * 60 * 60;
$expiration = wfTimestamp( TS_MW, $expires );
- $token = $this->generateToken( $this->mId . $this->mEmail . $expires );
+ $token = self::generateToken( $this->mId . $this->mEmail . $expires );
$hash = md5( $token );
$this->load();
$this->mEmailToken = $hash;
* @return \string Localized descriptive group name
*/
static function getGroupName( $group ) {
- global $wgMessageCache;
- $wgMessageCache->loadAllMessages();
$key = "group-$group";
$name = wfMsg( $key );
return $name == '' || wfEmptyMsg( $key, $name )
* @return \string Localized name for group member
*/
static function getGroupMember( $group ) {
- global $wgMessageCache;
- $wgMessageCache->loadAllMessages();
$key = "group-$group-member";
$name = wfMsg( $key );
return $name == '' || wfEmptyMsg( $key, $name )
* @return \types{\type{Title},\bool} Title of the page if it exists, false otherwise
*/
static function getGroupPage( $group ) {
- global $wgMessageCache;
- $wgMessageCache->loadAllMessages();
$page = wfMsgForContent( 'grouppage-' . $group );
if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) {
$title = Title::newFromText( $page );
* @return \string Localized description of the right
*/
static function getRightDescription( $right ) {
- global $wgMessageCache;
- $wgMessageCache->loadAllMessages();
$key = "right-$right";
$name = wfMsg( $key );
return $name == '' || wfEmptyMsg( $key, $name )
* @return \bool
*/
static function comparePasswords( $hash, $password, $userId = false ) {
- $m = false;
$type = substr( $hash, 0, 3 );
$result = false;
/**
* Add a newuser log entry for this user
+ *
* @param $byEmail Boolean: account made by email?
+ * @param $reason String: user supplied reason
*/
- public function addNewUserLogEntry( $byEmail = false ) {
- global $wgUser, $wgNewUserLog;
+ public function addNewUserLogEntry( $byEmail = false, $reason = '' ) {
+ global $wgUser, $wgContLang, $wgNewUserLog;
if( empty( $wgNewUserLog ) ) {
return true; // disabled
}
if( $this->getName() == $wgUser->getName() ) {
$action = 'create';
- $message = '';
} else {
$action = 'create2';
- $message = $byEmail
- ? wfMsgForContent( 'newuserlog-byemail' )
- : '';
+ if ( $byEmail ) {
+ if ( $reason === '' ) {
+ $reason = wfMsgForContent( 'newuserlog-byemail' );
+ } else {
+ $reason = $wgContLang->commaList( array(
+ $reason, wfMsgForContent( 'newuserlog-byemail' ) ) );
+ }
+ }
}
$log = new LogPage( 'newusers' );
$log->addEntry(
$action,
$this->getUserPage(),
- $message,
+ $reason,
array( $this->getId() )
);
return true;
* Used by things like CentralAuth and perhaps other authplugins.
*/
public function addNewUserLogEntryAutoCreate() {
- global $wgNewUserLog;
- if( empty( $wgNewUserLog ) ) {
+ global $wgNewUserLog, $wgLogAutocreatedAccounts;
+ if( !$wgNewUserLog || !$wgLogAutocreatedAccounts ) {
return true; // disabled
}
$log = new LogPage( 'newusers', false );
*
* Obviously, you still need to do server-side checking.
*
+ * NOTE: A combination of bugs in various browsers means that this function
+ * actually just returns array() unconditionally at the moment. May as
+ * well keep it around for when the browser bugs get fixed, though.
+ *
* @return array Array of HTML attributes suitable for feeding to
* Html::element(), directly or indirectly. (Don't feed to Xml::*()!
* That will potentially output invalid XHTML 1.0 Transitional, and will
# Note that the pattern requirement will always be satisfied if the
# input is empty, so we need required in all cases.
- $ret = array( 'required' );
+ #
+ # FIXME (bug 23769): This needs to not claim the password is required
+ # if e-mail confirmation is being used. Since HTML5 input validation
+ # is b0rked anyway in some browsers, just return nothing. When it's
+ # re-enabled, fix this code to not output required for e-mail
+ # registration.
+ #$ret = array( 'required' );
+ $ret = array();
# We can't actually do this right now, because Opera 9.6 will print out
# the entered password visibly in its error message! When other
return $ret;
}
+
+ /**
+ * Format the user message using a hook, a template, or, failing these, a static format.
+ * @param $subject String the subject of the message
+ * @param $text String the content of the message
+ * @param $signature String the signature, if provided.
+ */
+ static protected function formatUserMessage( $subject, $text, $signature ) {
+ if ( wfRunHooks( 'FormatUserMessage',
+ array( $subject, &$text, $signature ) ) ) {
+
+ $signature = empty($signature) ? "~~~~~" : "{$signature} ~~~~~";
+
+ $template = Title::newFromText( wfMsgForContent( 'usermessage-template' ) );
+ if ( !$template
+ || $template->getNamespace() !== NS_TEMPLATE
+ || !$template->exists() ) {
+ $text = "\n== $subject ==\n\n$text\n\n-- $signature";
+ } else {
+ $text = '{{'. $template->getText()
+ . " | subject=$subject | body=$text | signature=$signature }}";
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Leave a user a message
+ * @param $subject String the subject of the message
+ * @param $text String the message to leave
+ * @param $signature String Text to leave in the signature
+ * @param $summary String the summary for this change, defaults to
+ * "Leave system message."
+ * @param $editor User The user leaving the message, defaults to
+ * "{{MediaWiki:usermessage-editor}}"
+ * @param $flags Int default edit flags
+ *
+ * @return boolean true if it was successful
+ */
+ public function leaveUserMessage( $subject, $text, $signature = "",
+ $summary = null, $editor = null, $flags = 0 ) {
+ if ( !isset( $summary ) ) {
+ $summary = wfMsgForContent( 'usermessage-summary' );
+ }
+
+ if ( !isset( $editor ) ) {
+ $editor = User::newFromName( wfMsgForContent( 'usermessage-editor' ) );
+ if ( !$editor->isLoggedIn() ) {
+ $editor->addToDatabase();
+ }
+ }
+
+ $article = new Article( $this->getTalkPage() );
+ wfRunHooks( 'SetupUserMessageArticle',
+ array( $this, &$article, $subject, $text, $signature, $summary, $editor ) );
+
+
+ $text = self::formatUserMessage( $subject, $text, $signature );
+ $flags = $article->checkFlags( $flags );
+
+ if ( $flags & EDIT_UPDATE ) {
+ $text = $article->getContent() . $text;
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ try {
+ $status = $article->doEdit( $text, $summary, $flags, false, $editor );
+ } catch ( DBQueryError $e ) {
+ $status = Status::newFatal("DB Error");
+ }
+
+ if ( $status->isGood() ) {
+ // Set newtalk with the right user ID
+ $this->setNewtalk( true );
+ wfRunHooks( 'AfterUserMessage',
+ array( $this, $article, $summary, $text, $signature, $summary, $editor ) );
+ $dbw->commit();
+ } else {
+ // The article was concurrently created
+ wfDebug( __METHOD__ . ": Error ".$status->getWikiText() );
+ $dbw->rollback();
+ }
+
+ return $status->isGood();
+ }
}