11 require_once( 'WatchedItem.php' );
13 # Number of characters in user_token field
14 define( 'USER_TOKEN_LENGTH', 32 );
16 # Serialized record version
17 define( 'MW_USER_VERSION', 2 );
27 var $mId, $mName, $mPassword, $mEmail, $mNewtalk;
28 var $mEmailAuthenticated;
29 var $mRights, $mOptions;
30 var $mDataLoaded, $mNewpassword;
32 var $mBlockedby, $mBlockreason;
38 var $mVersion; // serialized version
40 /** Construct using User:loadDefaults() */
42 $this->loadDefaults();
43 $this->mVersion
= MW_USER_VERSION
;
47 * Static factory method
48 * @param string $name Username, validated by Title:newFromText()
52 function newFromName( $name ) {
53 # Force usernames to capital
55 $name = $wgContLang->ucfirst( $name );
57 # Clean up name according to title rules
58 $t = Title
::newFromText( $name );
63 # Reject various classes of invalid names
64 $canonicalName = $t->getText();
66 $canonicalName = $wgAuth->getCanonicalName( $t->getText() );
68 if( !User
::isValidUserName( $canonicalName ) ) {
73 $u->setName( $canonicalName );
74 $u->setId( $u->idFromName( $canonicalName ) );
79 * Factory method to fetch whichever use has a given email confirmation code.
80 * This code is generated when an account is created or its e-mail address
83 * If the code is invalid or has expired, returns NULL.
89 function newFromConfirmationCode( $code ) {
90 $dbr =& wfGetDB( DB_SLAVE
);
91 $name = $dbr->selectField( 'user', 'user_name', array(
92 'user_email_token' => md5( $code ),
93 'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
95 if( is_string( $name ) ) {
96 return User
::newFromName( $name );
103 * Serialze sleep function, for better cache efficiency and avoidance of
104 * silly "incomplete type" errors when skins are cached
107 return array( 'mId', 'mName', 'mPassword', 'mEmail', 'mNewtalk',
108 'mEmailAuthenticated', 'mRights', 'mOptions', 'mDataLoaded',
109 'mNewpassword', 'mBlockedby', 'mBlockreason', 'mTouched',
110 'mToken', 'mRealName', 'mHash', 'mGroups' );
114 * Get username given an id.
115 * @param integer $id Database user id
116 * @return string Nickname of a user
119 function whoIs( $id ) {
120 $dbr =& wfGetDB( DB_SLAVE
);
121 return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
125 * Get real username given an id.
126 * @param integer $id Database user id
127 * @return string Realname of a user
130 function whoIsReal( $id ) {
131 $dbr =& wfGetDB( DB_SLAVE
);
132 return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), 'User::whoIsReal' );
136 * Get database id given a user name
137 * @param string $name Nickname of a user
138 * @return integer|null Database user id (null: if non existent
141 function idFromName( $name ) {
142 $fname = "User::idFromName";
144 $nt = Title
::newFromText( $name );
145 if( is_null( $nt ) ) {
149 $dbr =& wfGetDB( DB_SLAVE
);
150 $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), $fname );
152 if ( $s === false ) {
160 * does the string match an anonymous IPv4 address?
162 * Note: We match \d{1,3}\.\d{1,3}\.\d{1,3}\.xxx as an anonymous IP
163 * address because the usemod software would "cloak" anonymous IP
164 * addresses like this, if we allowed accounts like this to be created
165 * new users could get the old edits of these anonymous users.
170 * @param string $name Nickname of a user
173 function isIP( $name ) {
174 return preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/",$name);
175 /*return preg_match("/^
176 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
177 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
178 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
179 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))
184 * Is the input a valid username?
186 * Checks if the input is a valid username, we don't want an empty string,
187 * an IP address, anything that containins slashes (would mess up subpages),
188 * is longer than the maximum allowed username size or doesn't begin with
191 * @param string $name
195 function isValidUserName( $name ) {
196 global $wgContLang, $wgMaxNameChars;
199 || User
::isIP( $name )
200 ||
strpos( $name, '/' ) !== false
201 ||
strlen( $name ) > $wgMaxNameChars
202 ||
$name != $wgContLang->ucfirst( $name ) )
209 * Is the input a valid password?
211 * @param string $password
215 function isValidPassword( $password ) {
216 global $wgMinimalPasswordLength;
217 return strlen( $password ) >= $wgMinimalPasswordLength;
221 * Does the string match roughly an email address ?
223 * There used to be a regular expression here, it got removed because it
224 * rejected valid addresses. Actually just check if there is '@' somewhere
225 * in the given address.
227 * @todo Check for RFC 2822 compilance
230 * @param string $addr email address
234 function isValidEmailAddr ( $addr ) {
235 return ( trim( $addr ) != '' ) &&
236 (false !== strpos( $addr, '@' ) );
240 * Count the number of edits of a user
242 * @param int $uid The user ID to check
245 function edits( $uid ) {
246 $fname = 'User::edits';
248 $dbr =& wfGetDB( DB_SLAVE
);
249 return $dbr->selectField(
250 'revision', 'count(*)',
251 array( 'rev_user' => $uid ),
257 * probably return a random password
258 * @return string probably a random password
260 * @todo Check what is doing really [AV]
262 function randomPassword() {
263 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
264 $l = strlen( $pwchars ) - 1;
266 $np = $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
267 $pwchars{mt_rand( 0, $l )} . chr( mt_rand(48, 57) ) .
268 $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
269 $pwchars{mt_rand( 0, $l )};
274 * Set properties to default
275 * Used at construction. It will load per language default settings only
276 * if we have an available language object.
278 function loadDefaults() {
281 $fname = 'User::loadDefaults' . $n;
282 wfProfileIn( $fname );
284 global $wgContLang, $wgDBname;
285 global $wgNamespacesToBeSearchedDefault;
288 $this->mNewtalk
= -1;
289 $this->mName
= false;
290 $this->mRealName
= $this->mEmail
= '';
291 $this->mEmailAuthenticated
= null;
292 $this->mPassword
= $this->mNewpassword
= '';
293 $this->mRights
= array();
294 $this->mGroups
= array();
295 $this->mOptions
= User
::getDefaultOptions();
297 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
298 $this->mOptions
['searchNs'.$nsnum] = $val;
300 unset( $this->mSkin
);
301 $this->mDataLoaded
= false;
302 $this->mBlockedby
= -1; # Unset
303 $this->setToken(); # Random
304 $this->mHash
= false;
306 if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) {
307 $this->mTouched
= wfTimestamp( TS_MW
, $_COOKIE[$wgDBname.'LoggedOut'] );
310 $this->mTouched
= '0'; # Allow any pages to be cached
313 wfProfileOut( $fname );
317 * Combine the language default options with any site-specific options
318 * and add the default language variants.
324 function getDefaultOptions() {
326 * Site defaults will override the global/language defaults
328 global $wgContLang, $wgDefaultUserOptions;
329 $defOpt = $wgDefaultUserOptions +
$wgContLang->getDefaultUserOptions();
332 * default language setting
334 $variant = $wgContLang->getPreferredVariant();
335 $defOpt['variant'] = $variant;
336 $defOpt['language'] = $variant;
342 * Get a given default option value.
349 function getDefaultOption( $opt ) {
350 $defOpts = User
::getDefaultOptions();
351 if( isset( $defOpts[$opt] ) ) {
352 return $defOpts[$opt];
359 * Get blocking information
361 * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
362 * non-critical checks are done against slaves. Check when actually saving should be done against
365 * Note that even if $bFromSlave is false, the check is done first against slave, then master.
366 * The logic is that if blocked on slave, we'll assume it's either blocked on master or
367 * just slightly outta sync and soon corrected - safer to block slightly more that less.
368 * And it's cheaper to check slave first, then master if needed, than master always.
370 function getBlockedStatus( $bFromSlave = true ) {
371 global $wgBlockCache, $wgProxyList, $wgEnableSorbs, $wgProxyWhitelist;
373 if ( -1 != $this->mBlockedby
) {
374 wfDebug( "User::getBlockedStatus: already loaded.\n" );
378 $fname = 'User::getBlockedStatus';
379 wfProfileIn( $fname );
380 wfDebug( "$fname: checking...\n" );
382 $this->mBlockedby
= 0;
386 $block = new Block();
387 $block->forUpdate( $bFromSlave );
388 if ( $block->load( $ip , $this->mId
) ) {
389 wfDebug( "$fname: Found block.\n" );
390 $this->mBlockedby
= $block->mBy
;
391 $this->mBlockreason
= $block->mReason
;
392 if ( $this->isLoggedIn() ) {
393 $this->spreadBlock();
396 wfDebug( "$fname: No block.\n" );
400 if ( !$this->mBlockedby
) {
401 # Check first against slave, and optionally from master.
402 wfDebug( "$fname: Checking range blocks\n" );
403 $block = $wgBlockCache->get( $ip, true );
404 if ( !$block && !$bFromSlave )
406 # Not blocked: check against master, to make sure.
407 $wgBlockCache->clearLocal( );
408 $block = $wgBlockCache->get( $ip, false );
410 if ( $block !== false ) {
411 $this->mBlockedby
= $block->mBy
;
412 $this->mBlockreason
= $block->mReason
;
417 if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) {
420 if ( array_key_exists( $ip, $wgProxyList ) ) {
421 $this->mBlockedby
= wfMsg( 'proxyblocker' );
422 $this->mBlockreason
= wfMsg( 'proxyblockreason' );
426 if ( !$this->mBlockedby
&& $wgEnableSorbs && !$this->getID() ) {
427 if ( $this->inSorbsBlacklist( $ip ) ) {
428 $this->mBlockedby
= wfMsg( 'sorbs' );
429 $this->mBlockreason
= wfMsg( 'sorbsreason' );
435 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
437 wfProfileOut( $fname );
440 function inSorbsBlacklist( $ip ) {
441 global $wgEnableSorbs;
442 return $wgEnableSorbs &&
443 $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
446 function inOpmBlacklist( $ip ) {
448 return $wgEnableOpm &&
449 $this->inDnsBlacklist( $ip, 'opm.blitzed.org.' );
452 function inDnsBlacklist( $ip, $base ) {
453 $fname = 'User::inDnsBlacklist';
454 wfProfileIn( $fname );
459 if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
461 for ( $i=4; $i>=1; $i-- ) {
462 $host .= $m[$i] . '.';
467 $ipList = gethostbynamel( $host );
470 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
473 wfDebug( "Requested $host, not found in $base.\n" );
477 wfProfileOut( $fname );
482 * Primitive rate limits: enforce maximum actions per time period
483 * to put a brake on flooding.
485 * Note: when using a shared cache like memcached, IP-address
486 * last-hit counters will be shared across wikis.
488 * @return bool true if a rate limiter was tripped
491 function pingLimiter( $action='edit' ) {
492 global $wgRateLimits;
493 if( !isset( $wgRateLimits[$action] ) ) {
496 if( $this->isAllowed( 'delete' ) ) {
501 global $wgMemc, $wgDBname, $wgRateLimitLog;
502 $fname = 'User::pingLimiter';
503 wfProfileIn( $fname );
505 $limits = $wgRateLimits[$action];
507 $id = $this->getId();
510 if( isset( $limits['anon'] ) && $id == 0 ) {
511 $keys["$wgDBname:limiter:$action:anon"] = $limits['anon'];
514 if( isset( $limits['user'] ) && $id != 0 ) {
515 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['user'];
517 if( $this->isNewbie() ) {
518 if( isset( $limits['newbie'] ) && $id != 0 ) {
519 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['newbie'];
521 if( isset( $limits['ip'] ) ) {
522 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
524 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
525 $subnet = $matches[1];
526 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
531 foreach( $keys as $key => $limit ) {
532 list( $max, $period ) = $limit;
533 $summary = "(limit $max in {$period}s)";
534 $count = $wgMemc->get( $key );
536 if( $count > $max ) {
537 wfDebug( "$fname: tripped! $key at $count $summary\n" );
538 if( $wgRateLimitLog ) {
539 @error_log
( wfTimestamp( TS_MW
) . ' ' . $wgDBname . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
543 wfDebug( "$fname: ok. $key at $count $summary\n" );
546 wfDebug( "$fname: adding record for $key $summary\n" );
547 $wgMemc->add( $key, 1, intval( $period ) );
549 $wgMemc->incr( $key );
552 wfProfileOut( $fname );
557 * Check if user is blocked
558 * @return bool True if blocked, false otherwise
560 function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
561 wfDebug( "User::isBlocked: enter\n" );
562 $this->getBlockedStatus( $bFromSlave );
563 return $this->mBlockedby
!== 0;
567 * Check if user is blocked from editing a particular article
569 function isBlockedFrom( $title, $bFromSlave = false ) {
570 global $wgBlockAllowsUTEdit;
571 $fname = 'User::isBlockedFrom';
572 wfProfileIn( $fname );
573 wfDebug( "$fname: enter\n" );
575 if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
576 $title->getNamespace() == NS_USER_TALK
)
579 wfDebug( "$fname: self-talk page, ignoring any blocks\n" );
581 wfDebug( "$fname: asking isBlocked()\n" );
582 $blocked = $this->isBlocked( $bFromSlave );
584 wfProfileOut( $fname );
589 * Get name of blocker
590 * @return string name of blocker
592 function blockedBy() {
593 $this->getBlockedStatus();
594 return $this->mBlockedby
;
598 * Get blocking reason
599 * @return string Blocking reason
601 function blockedFor() {
602 $this->getBlockedStatus();
603 return $this->mBlockreason
;
607 * Initialise php session
609 function SetupSession() {
610 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
611 if( $wgSessionsInMemcached ) {
612 require_once( 'MemcachedSessions.php' );
613 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
614 # If it's left on 'user' or another setting from another
615 # application, it will end up failing. Try to recover.
616 ini_set ( 'session.save_handler', 'files' );
618 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
619 session_cache_limiter( 'private, must-revalidate' );
624 * Create a new user object using data from session
627 function loadFromSession() {
628 global $wgMemc, $wgDBname;
630 if ( isset( $_SESSION['wsUserID'] ) ) {
631 if ( 0 != $_SESSION['wsUserID'] ) {
632 $sId = $_SESSION['wsUserID'];
636 } else if ( isset( $_COOKIE["{$wgDBname}UserID"] ) ) {
637 $sId = intval( $_COOKIE["{$wgDBname}UserID"] );
638 $_SESSION['wsUserID'] = $sId;
642 if ( isset( $_SESSION['wsUserName'] ) ) {
643 $sName = $_SESSION['wsUserName'];
644 } else if ( isset( $_COOKIE["{$wgDBname}UserName"] ) ) {
645 $sName = $_COOKIE["{$wgDBname}UserName"];
646 $_SESSION['wsUserName'] = $sName;
651 $passwordCorrect = FALSE;
652 $user = $wgMemc->get( $key = "$wgDBname:user:id:$sId" );
653 if( !is_object( $user ) ||
$user->mVersion
< MW_USER_VERSION
) {
654 # Expire old serialized objects; they may be corrupt.
657 if($makenew = !$user) {
658 wfDebug( "User::loadFromSession() unable to load from memcached\n" );
661 $user->loadFromDatabase();
663 wfDebug( "User::loadFromSession() got from cache!\n" );
666 if ( isset( $_SESSION['wsToken'] ) ) {
667 $passwordCorrect = $_SESSION['wsToken'] == $user->mToken
;
668 } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) {
669 $passwordCorrect = $user->mToken
== $_COOKIE["{$wgDBname}Token"];
671 return new User(); # Can't log in from session
674 if ( ( $sName == $user->mName
) && $passwordCorrect ) {
676 if($wgMemc->set( $key, $user ))
677 wfDebug( "User::loadFromSession() successfully saved user\n" );
679 wfDebug( "User::loadFromSession() unable to save to memcached\n" );
683 return new User(); # Can't log in from session
687 * Load a user from the database
689 function loadFromDatabase() {
690 global $wgCommandLineMode;
691 $fname = "User::loadFromDatabase";
693 # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress
694 # loading in a command line script, don't assume all command line scripts need it like this
695 #if ( $this->mDataLoaded || $wgCommandLineMode ) {
696 if ( $this->mDataLoaded
) {
701 $this->mId
= intval( $this->mId
);
703 /** Anonymous user */
706 $this->mRights
= $this->getGroupPermissions( array( '*' ) );
707 $this->mDataLoaded
= true;
709 } # the following stuff is for non-anonymous users only
711 $dbr =& wfGetDB( DB_SLAVE
);
712 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
713 'user_email_authenticated',
714 'user_real_name','user_options','user_touched', 'user_token' ),
715 array( 'user_id' => $this->mId
), $fname );
717 if ( $s !== false ) {
718 $this->mName
= $s->user_name
;
719 $this->mEmail
= $s->user_email
;
720 $this->mEmailAuthenticated
= wfTimestampOrNull( TS_MW
, $s->user_email_authenticated
);
721 $this->mRealName
= $s->user_real_name
;
722 $this->mPassword
= $s->user_password
;
723 $this->mNewpassword
= $s->user_newpassword
;
724 $this->decodeOptions( $s->user_options
);
725 $this->mTouched
= wfTimestamp(TS_MW
,$s->user_touched
);
726 $this->mToken
= $s->user_token
;
728 $res = $dbr->select( 'user_groups',
730 array( 'ug_user' => $this->mId
),
732 $this->mGroups
= array();
733 while( $row = $dbr->fetchObject( $res ) ) {
734 $this->mGroups
[] = $row->ug_group
;
736 $effectiveGroups = array_merge( array( '*', 'user' ), $this->mGroups
);
737 $this->mRights
= $this->getGroupPermissions( $effectiveGroups );
740 $this->mDataLoaded
= true;
743 function getID() { return $this->mId
; }
744 function setID( $v ) {
746 $this->mDataLoaded
= false;
750 $this->loadFromDatabase();
751 if ( $this->mName
=== false ) {
752 $this->mName
= wfGetIP();
757 function setName( $str ) {
758 $this->loadFromDatabase();
764 * Return the title dbkey form of the name, for eg user pages.
768 function getTitleKey() {
769 return str_replace( ' ', '_', $this->getName() );
772 function getNewtalk() {
774 $fname = 'User::getNewtalk';
775 $this->loadFromDatabase();
777 # Load the newtalk status if it is unloaded (mNewtalk=-1)
778 if( $this->mNewtalk
== -1 ) {
779 $this->mNewtalk
= 0; # reset talk page status
781 # Check memcached separately for anons, who have no
782 # entire User object stored in there.
784 global $wgDBname, $wgMemc;
785 $key = "$wgDBname:newtalk:ip:" . $this->getName();
786 $newtalk = $wgMemc->get( $key );
787 if( is_integer( $newtalk ) ) {
788 $this->mNewtalk
= $newtalk ?
1 : 0;
789 return (bool)$this->mNewtalk
;
793 $dbr =& wfGetDB( DB_SLAVE
);
794 if ( $wgUseEnotif ) {
795 $res = $dbr->select( 'watchlist',
797 array( 'wl_title' => $this->getTitleKey(),
798 'wl_namespace' => NS_USER_TALK
,
799 'wl_user' => $this->mId
,
800 'wl_notificationtimestamp ' . $dbr->notNullTimestamp() ),
801 'User::getNewtalk' );
802 if( $dbr->numRows($res) > 0 ) {
805 $dbr->freeResult( $res );
806 } elseif ( $this->mId
) {
807 $res = $dbr->select( 'user_newtalk', 1, array( 'user_id' => $this->mId
), $fname );
809 if ( $dbr->numRows($res)>0 ) {
812 $dbr->freeResult( $res );
814 $res = $dbr->select( 'user_newtalk', 1, array( 'user_ip' => $this->getName() ), $fname );
815 $this->mNewtalk
= $dbr->numRows( $res ) > 0 ?
1 : 0;
816 $dbr->freeResult( $res );
820 $wgMemc->set( $key, $this->mNewtalk
, time() ); // + 1800 );
824 return ( 0 != $this->mNewtalk
);
827 function setNewtalk( $val ) {
828 $this->loadFromDatabase();
829 $this->mNewtalk
= $val;
830 $this->invalidateCache();
833 function invalidateCache() {
834 global $wgClockSkewFudge;
835 $this->loadFromDatabase();
836 $this->mTouched
= wfTimestamp(TS_MW
, time() +
$wgClockSkewFudge );
837 # Don't forget to save the options after this or
838 # it won't take effect!
841 function validateCache( $timestamp ) {
842 $this->loadFromDatabase();
843 return ($timestamp >= $this->mTouched
);
847 * Encrypt a password.
848 * It can eventuall salt a password @see User::addSalt()
849 * @param string $p clear Password.
850 * @return string Encrypted password.
852 function encryptPassword( $p ) {
853 return wfEncryptPassword( $this->mId
, $p );
856 # Set the password and reset the random token
857 function setPassword( $str ) {
858 $this->loadFromDatabase();
860 $this->mPassword
= $this->encryptPassword( $str );
861 $this->mNewpassword
= '';
864 # Set the random token (used for persistent authentication)
865 function setToken( $token = false ) {
866 global $wgSecretKey, $wgProxyKey, $wgDBname;
868 if ( $wgSecretKey ) {
870 } elseif ( $wgProxyKey ) {
875 $this->mToken
= md5( $key . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId
);
877 $this->mToken
= $token;
882 function setCookiePassword( $str ) {
883 $this->loadFromDatabase();
884 $this->mCookiePassword
= md5( $str );
887 function setNewpassword( $str ) {
888 $this->loadFromDatabase();
889 $this->mNewpassword
= $this->encryptPassword( $str );
892 function getEmail() {
893 $this->loadFromDatabase();
894 return $this->mEmail
;
897 function getEmailAuthenticationTimestamp() {
898 $this->loadFromDatabase();
899 return $this->mEmailAuthenticated
;
902 function setEmail( $str ) {
903 $this->loadFromDatabase();
904 $this->mEmail
= $str;
907 function getRealName() {
908 $this->loadFromDatabase();
909 return $this->mRealName
;
912 function setRealName( $str ) {
913 $this->loadFromDatabase();
914 $this->mRealName
= $str;
917 function getOption( $oname ) {
918 $this->loadFromDatabase();
919 if ( array_key_exists( $oname, $this->mOptions
) ) {
920 return trim( $this->mOptions
[$oname] );
926 function setOption( $oname, $val ) {
927 $this->loadFromDatabase();
928 if ( $oname == 'skin' ) {
929 # Clear cached skin, so the new one displays immediately in Special:Preferences
930 unset( $this->mSkin
);
932 $this->mOptions
[$oname] = $val;
933 $this->invalidateCache();
936 function getRights() {
937 $this->loadFromDatabase();
938 return $this->mRights
;
942 * Get the list of explicit group memberships this user has.
943 * The implicit * and user groups are not included.
944 * @return array of strings
946 function getGroups() {
947 $this->loadFromDatabase();
948 return $this->mGroups
;
952 * Get the list of implicit group memberships this user has.
953 * This includes all explicit groups, plus 'user' if logged in
954 * and '*' for all accounts.
955 * @return array of strings
957 function getEffectiveGroups() {
958 $base = array( '*' );
959 if( $this->isLoggedIn() ) {
962 return array_merge( $base, $this->getGroups() );
966 * Remove the user from the given group.
967 * This takes immediate effect.
970 function addGroup( $group ) {
971 $dbw =& wfGetDB( DB_MASTER
);
972 $dbw->insert( 'user_groups',
974 'ug_user' => $this->getID(),
975 'ug_group' => $group,
980 $this->mGroups
= array_merge( $this->mGroups
, array( $group ) );
981 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
983 $this->invalidateCache();
984 $this->saveSettings();
988 * Remove the user from the given group.
989 * This takes immediate effect.
992 function removeGroup( $group ) {
993 $dbw =& wfGetDB( DB_MASTER
);
994 $dbw->delete( 'user_groups',
996 'ug_user' => $this->getID(),
997 'ug_group' => $group,
999 'User::removeGroup' );
1001 $this->mGroups
= array_diff( $this->mGroups
, array( $group ) );
1002 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1004 $this->invalidateCache();
1005 $this->saveSettings();
1010 * A more legible check for non-anonymousness.
1011 * Returns true if the user is not an anonymous visitor.
1015 function isLoggedIn() {
1016 return( $this->getID() != 0 );
1020 * A more legible check for anonymousness.
1021 * Returns true if the user is an anonymous visitor.
1026 return !$this->isLoggedIn();
1030 * Check if a user is sysop
1031 * Die with backtrace. Use User:isAllowed() instead.
1034 function isSysop() {
1035 return $this->isAllowed( 'protect' );
1039 function isDeveloper() {
1040 return $this->isAllowed( 'siteadmin' );
1044 function isBureaucrat() {
1045 return $this->isAllowed( 'makesysop' );
1049 * Whether the user is a bot
1050 * @todo need to be migrated to the new user level management sytem
1053 $this->loadFromDatabase();
1054 return in_array( 'bot', $this->mRights
);
1058 * Check if user is allowed to access a feature / make an action
1059 * @param string $action Action to be checked (see $wgAvailableRights in Defines.php for possible actions).
1060 * @return boolean True: action is allowed, False: action should not be allowed
1062 function isAllowed($action='') {
1063 $this->loadFromDatabase();
1064 return in_array( $action , $this->mRights
);
1068 * Load a skin if it doesn't exist or return it
1069 * @todo FIXME : need to check the old failback system [AV]
1071 function &getSkin() {
1072 global $IP, $wgRequest;
1073 if ( ! isset( $this->mSkin
) ) {
1074 $fname = 'User::getSkin';
1075 wfProfileIn( $fname );
1077 # get all skin names available
1078 $skinNames = Skin
::getSkinNames();
1081 $userSkin = $this->getOption( 'skin' );
1082 $userSkin = $wgRequest->getText('useskin', $userSkin);
1083 if ( $userSkin == '' ) { $userSkin = 'standard'; }
1085 if ( !isset( $skinNames[$userSkin] ) ) {
1086 # in case the user skin could not be found find a replacement
1090 2 => 'CologneBlue');
1091 # if phptal is enabled we should have monobook skin that
1092 # superseed the good old SkinStandard.
1093 if ( isset( $skinNames['monobook'] ) ) {
1094 $fallback[0] = 'MonoBook';
1097 if(is_numeric($userSkin) && isset( $fallback[$userSkin]) ){
1098 $sn = $fallback[$userSkin];
1103 # The user skin is available
1104 $sn = $skinNames[$userSkin];
1107 # Grab the skin class and initialise it. Each skin checks for PHPTal
1108 # and will not load if it's not enabled.
1109 require_once( $IP.'/skins/'.$sn.'.php' );
1111 # Check if we got if not failback to default skin
1112 $className = 'Skin'.$sn;
1113 if( !class_exists( $className ) ) {
1114 # DO NOT die if the class isn't found. This breaks maintenance
1115 # scripts and can cause a user account to be unrecoverable
1116 # except by SQL manipulation if a previously valid skin name
1117 # is no longer valid.
1118 $className = 'SkinStandard';
1119 require_once( $IP.'/skins/Standard.php' );
1121 $this->mSkin
=& new $className;
1122 wfProfileOut( $fname );
1124 return $this->mSkin
;
1128 * @param string $title Article title to look at
1132 * Check watched status of an article
1133 * @return bool True if article is watched
1135 function isWatched( $title ) {
1136 $wl = WatchedItem
::fromUserTitle( $this, $title );
1137 return $wl->isWatched();
1143 function addWatch( $title ) {
1144 $wl = WatchedItem
::fromUserTitle( $this, $title );
1146 $this->invalidateCache();
1150 * Stop watching an article
1152 function removeWatch( $title ) {
1153 $wl = WatchedItem
::fromUserTitle( $this, $title );
1155 $this->invalidateCache();
1159 * Clear the user's notification timestamp for the given title.
1160 * If e-notif e-mails are on, they will receive notification mails on
1161 * the next change of the page if it's watched etc.
1163 function clearNotification( &$title ) {
1164 global $wgUser, $wgUseEnotif;
1166 if ( !$wgUseEnotif ) {
1170 $userid = $this->getID();
1175 // Only update the timestamp if the page is being watched.
1176 // The query to find out if it is watched is cached both in memcached and per-invocation,
1177 // and when it does have to be executed, it can be on a slave
1178 // If this is the user's newtalk page, we always update the timestamp
1179 if ($title->getNamespace() == NS_USER_TALK
&&
1180 $title->getText() == $wgUser->getName())
1183 } elseif ( $this->getID() == $wgUser->getID() ) {
1184 $watched = $title->userIsWatching();
1189 // If the page is watched by the user (or may be watched), update the timestamp on any
1190 // any matching rows
1192 $dbw =& wfGetDB( DB_MASTER
);
1193 $success = $dbw->update( 'watchlist',
1195 'wl_notificationtimestamp' => NULL
1196 ), array( /* WHERE */
1197 'wl_title' => $title->getDBkey(),
1198 'wl_namespace' => $title->getNamespace(),
1199 'wl_user' => $this->getID()
1200 ), 'User::clearLastVisited'
1208 * Resets all of the given user's page-change notification timestamps.
1209 * If e-notif e-mails are on, they will receive notification mails on
1210 * the next change of any watched page.
1212 * @param int $currentUser user ID number
1215 function clearAllNotifications( $currentUser ) {
1216 global $wgUseEnotif;
1217 if ( !$wgUseEnotif ) {
1220 if( $currentUser != 0 ) {
1222 $dbw =& wfGetDB( DB_MASTER
);
1223 $success = $dbw->update( 'watchlist',
1225 'wl_notificationtimestamp' => 0
1226 ), array( /* WHERE */
1227 'wl_user' => $currentUser
1228 ), 'UserMailer::clearAll'
1231 # we also need to clear here the "you have new message" notification for the own user_talk page
1232 # This is cleared one page view later in Article::viewUpdates();
1238 * @return string Encoding options
1240 function encodeOptions() {
1242 foreach ( $this->mOptions
as $oname => $oval ) {
1243 array_push( $a, $oname.'='.$oval );
1245 $s = implode( "\n", $a );
1252 function decodeOptions( $str ) {
1253 $a = explode( "\n", $str );
1254 foreach ( $a as $s ) {
1255 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
1256 $this->mOptions
[$m[1]] = $m[2];
1261 function setCookies() {
1262 global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgDBname;
1263 if ( 0 == $this->mId
) return;
1264 $this->loadFromDatabase();
1265 $exp = time() +
$wgCookieExpiration;
1267 $_SESSION['wsUserID'] = $this->mId
;
1268 setcookie( $wgDBname.'UserID', $this->mId
, $exp, $wgCookiePath, $wgCookieDomain );
1270 $_SESSION['wsUserName'] = $this->getName();
1271 setcookie( $wgDBname.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain );
1273 $_SESSION['wsToken'] = $this->mToken
;
1274 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
1275 setcookie( $wgDBname.'Token', $this->mToken
, $exp, $wgCookiePath, $wgCookieDomain );
1277 setcookie( $wgDBname.'Token', '', time() - 3600 );
1283 * It will clean the session cookie
1286 global $wgCookiePath, $wgCookieDomain, $wgDBname;
1287 $this->loadDefaults();
1288 $this->setLoaded( true );
1290 $_SESSION['wsUserID'] = 0;
1292 setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1293 setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1295 # Remember when user logged out, to prevent seeing cached pages
1296 setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() +
86400, $wgCookiePath, $wgCookieDomain );
1300 * Save object settings into database
1302 function saveSettings() {
1303 global $wgMemc, $wgDBname, $wgUseEnotif;
1304 $fname = 'User::saveSettings';
1306 if ( wfReadOnly() ) { return; }
1307 $this->saveNewtalk();
1308 if ( 0 == $this->mId
) { return; }
1310 $dbw =& wfGetDB( DB_MASTER
);
1311 $dbw->update( 'user',
1313 'user_name' => $this->mName
,
1314 'user_password' => $this->mPassword
,
1315 'user_newpassword' => $this->mNewpassword
,
1316 'user_real_name' => $this->mRealName
,
1317 'user_email' => $this->mEmail
,
1318 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1319 'user_options' => $this->encodeOptions(),
1320 'user_touched' => $dbw->timestamp($this->mTouched
),
1321 'user_token' => $this->mToken
1322 ), array( /* WHERE */
1323 'user_id' => $this->mId
1326 $wgMemc->delete( "$wgDBname:user:id:$this->mId" );
1330 * Save value of new talk flag.
1332 function saveNewtalk() {
1333 global $wgDBname, $wgMemc, $wgUseEnotif;
1335 $fname = 'User::saveNewtalk';
1339 if ( wfReadOnly() ) { return ; }
1340 $dbr =& wfGetDB( DB_SLAVE
);
1341 $dbw =& wfGetDB( DB_MASTER
);
1343 if ( $wgUseEnotif ) {
1344 if ( ! $this->getNewtalk() ) {
1345 # Delete the watchlist entry for user_talk page X watched by user X
1346 $dbw->delete( 'watchlist',
1347 array( 'wl_user' => $this->mId
,
1348 'wl_title' => $this->getTitleKey(),
1349 'wl_namespace' => NS_USER_TALK
),
1351 if ( $dbw->affectedRows() ) {
1355 # Anon users have a separate memcache space for newtalk
1356 # since they don't store their own info. Trim...
1357 $wgMemc->delete( "$wgDBname:newtalk:ip:" . $this->getName() );
1361 if ($this->getID() != 0) {
1363 $value = $this->getID();
1367 $value = $this->getName();
1368 $key = "$wgDBname:newtalk:ip:$value";
1371 $dbr =& wfGetDB( DB_SLAVE
);
1372 $dbw =& wfGetDB( DB_MASTER
);
1374 $res = $dbr->selectField('user_newtalk', $field,
1375 array($field => $value), $fname);
1378 if ($res !== false && $this->mNewtalk
== 0) {
1379 $dbw->delete('user_newtalk', array($field => $value), $fname);
1381 $wgMemc->set( $key, 0 );
1383 } else if ($res === false && $this->mNewtalk
== 1) {
1384 $dbw->insert('user_newtalk', array($field => $value), $fname);
1386 $wgMemc->set( $key, 1 );
1393 # Update user_touched, so that newtalk notifications in the client cache are invalidated
1394 if ( $changed && $this->getID() ) {
1395 $dbw->update('user',
1396 /*SET*/ array( 'user_touched' => $this->mTouched
),
1397 /*WHERE*/ array( 'user_id' => $this->getID() ),
1399 $wgMemc->set( "$wgDBname:user:id:{$this->mId}", $this, 86400 );
1404 * Checks if a user with the given name exists, returns the ID
1406 function idForName() {
1407 $fname = 'User::idForName';
1410 $s = trim( $this->getName() );
1411 if ( 0 == strcmp( '', $s ) ) return 0;
1413 $dbr =& wfGetDB( DB_SLAVE
);
1414 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), $fname );
1415 if ( $id === false ) {
1422 * Add user object to the database
1424 function addToDatabase() {
1425 $fname = 'User::addToDatabase';
1426 $dbw =& wfGetDB( DB_MASTER
);
1427 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
1428 $dbw->insert( 'user',
1430 'user_id' => $seqVal,
1431 'user_name' => $this->mName
,
1432 'user_password' => $this->mPassword
,
1433 'user_newpassword' => $this->mNewpassword
,
1434 'user_email' => $this->mEmail
,
1435 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1436 'user_real_name' => $this->mRealName
,
1437 'user_options' => $this->encodeOptions(),
1438 'user_token' => $this->mToken
1441 $this->mId
= $dbw->insertId();
1444 function spreadBlock() {
1445 # If the (non-anonymous) user is blocked, this function will block any IP address
1446 # that they successfully log on from.
1447 $fname = 'User::spreadBlock';
1449 wfDebug( "User:spreadBlock()\n" );
1450 if ( $this->mId
== 0 ) {
1454 $userblock = Block
::newFromDB( '', $this->mId
);
1455 if ( !$userblock->isValid() ) {
1459 # Check if this IP address is already blocked
1460 $ipblock = Block
::newFromDB( wfGetIP() );
1461 if ( $ipblock->isValid() ) {
1462 # If the user is already blocked. Then check if the autoblock would
1463 # excede the user block. If it would excede, then do nothing, else
1464 # prolong block time
1465 if ($userblock->mExpiry
&&
1466 ($userblock->mExpiry
< Block
::getAutoblockExpiry($ipblock->mTimestamp
))) {
1469 # Just update the timestamp
1470 $ipblock->updateTimestamp();
1474 # Make a new block object with the desired properties
1475 wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
1476 $ipblock->mAddress
= wfGetIP();
1477 $ipblock->mUser
= 0;
1478 $ipblock->mBy
= $userblock->mBy
;
1479 $ipblock->mReason
= wfMsg( 'autoblocker', $this->getName(), $userblock->mReason
);
1480 $ipblock->mTimestamp
= wfTimestampNow();
1481 $ipblock->mAuto
= 1;
1482 # If the user is already blocked with an expiry date, we don't
1483 # want to pile on top of that!
1484 if($userblock->mExpiry
) {
1485 $ipblock->mExpiry
= min ( $userblock->mExpiry
, Block
::getAutoblockExpiry( $ipblock->mTimestamp
));
1487 $ipblock->mExpiry
= Block
::getAutoblockExpiry( $ipblock->mTimestamp
);
1495 function getPageRenderingHash() {
1498 return $this->mHash
;
1501 // stubthreshold is only included below for completeness,
1502 // it will always be 0 when this function is called by parsercache.
1504 $confstr = $this->getOption( 'math' );
1505 $confstr .= '!' . $this->getOption( 'stubthreshold' );
1506 $confstr .= '!' . $this->getOption( 'date' );
1507 $confstr .= '!' . $this->getOption( 'numberheadings' );
1508 $confstr .= '!' . $this->getOption( 'language' );
1509 $confstr .= '!' . $this->getOption( 'thumbsize' );
1510 // add in language specific options, if any
1511 $extra = $wgContLang->getExtraHashOptions();
1514 $this->mHash
= $confstr;
1518 function isAllowedToCreateAccount() {
1519 return $this->isAllowed( 'createaccount' ) && !$this->isBlocked();
1523 * Set mDataLoaded, return previous value
1524 * Use this to prevent DB access in command-line scripts or similar situations
1526 function setLoaded( $loaded ) {
1527 return wfSetVar( $this->mDataLoaded
, $loaded );
1531 * Get this user's personal page title.
1536 function getUserPage() {
1537 return Title
::makeTitle( NS_USER
, $this->getName() );
1541 * Get this user's talk page title.
1546 function getTalkPage() {
1547 $title = $this->getUserPage();
1548 return $title->getTalkPage();
1554 function getMaxID() {
1555 $dbr =& wfGetDB( DB_SLAVE
);
1556 return $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
1560 * Determine whether the user is a newbie. Newbies are either
1561 * anonymous IPs, or the 1% most recently created accounts.
1562 * Bots and sysops are excluded.
1563 * @return bool True if it is a newbie.
1565 function isNewbie() {
1566 return $this->isAnon() ||
$this->mId
> User
::getMaxID() * 0.99 && !$this->isAllowed( 'delete' ) && !$this->isBot();
1570 * Check to see if the given clear-text password is one of the accepted passwords
1571 * @param string $password User password.
1572 * @return bool True if the given password is correct otherwise False.
1574 function checkPassword( $password ) {
1575 global $wgAuth, $wgMinimalPasswordLength;
1576 $this->loadFromDatabase();
1578 // Even though we stop people from creating passwords that
1579 // are shorter than this, doesn't mean people wont be able
1580 // to. Certain authentication plugins do NOT want to save
1581 // domain passwords in a mysql database, so we should
1582 // check this (incase $wgAuth->strict() is false).
1583 if( strlen( $password ) < $wgMinimalPasswordLength ) {
1587 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
1589 } elseif( $wgAuth->strict() ) {
1590 /* Auth plugin doesn't allow local authentication */
1593 $ep = $this->encryptPassword( $password );
1594 if ( 0 == strcmp( $ep, $this->mPassword
) ) {
1596 } elseif ( ($this->mNewpassword
!= '') && (0 == strcmp( $ep, $this->mNewpassword
)) ) {
1598 } elseif ( function_exists( 'iconv' ) ) {
1599 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
1600 # Check for this with iconv
1601 $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
1602 if ( 0 == strcmp( $cp1252hash, $this->mPassword
) ) {
1610 * Initialize (if necessary) and return a session token value
1611 * which can be used in edit forms to show that the user's
1612 * login credentials aren't being hijacked with a foreign form
1615 * @param mixed $salt - Optional function-specific data for hash.
1616 * Use a string or an array of strings.
1620 function editToken( $salt = '' ) {
1621 if( !isset( $_SESSION['wsEditToken'] ) ) {
1622 $token = $this->generateToken();
1623 $_SESSION['wsEditToken'] = $token;
1625 $token = $_SESSION['wsEditToken'];
1627 if( is_array( $salt ) ) {
1628 $salt = implode( '|', $salt );
1630 return md5( $token . $salt );
1634 * Generate a hex-y looking random token for various uses.
1635 * Could be made more cryptographically sure if someone cares.
1638 function generateToken( $salt = '' ) {
1639 $token = dechex( mt_rand() ) . dechex( mt_rand() );
1640 return md5( $token . $salt );
1644 * Check given value against the token value stored in the session.
1645 * A match should confirm that the form was submitted from the
1646 * user's own login session, not a form submission from a third-party
1649 * @param string $val - the input value to compare
1650 * @param string $salt - Optional function-specific data for hash
1654 function matchEditToken( $val, $salt = '' ) {
1658 if ( !isset( $_SESSION['wsEditToken'] ) ) {
1659 $logfile = '/home/wikipedia/logs/session_debug/session.log';
1660 $mckey = memsess_key( session_id() );
1661 $uname = @posix_uname();
1662 $msg = "wsEditToken not set!\n" .
1663 'apache server=' . $uname['nodename'] . "\n" .
1664 'session_id = ' . session_id() . "\n" .
1665 '$_SESSION=' . var_export( $_SESSION, true ) . "\n" .
1666 '$_COOKIE=' . var_export( $_COOKIE, true ) . "\n" .
1667 "mc get($mckey) = " . var_export( $wgMemc->get( $mckey ), true ) . "\n\n\n";
1669 @error_log( $msg, 3, $logfile );
1672 return ( $val == $this->editToken( $salt ) );
1676 * Generate a new e-mail confirmation token and send a confirmation
1677 * mail to the user's given address.
1679 * @return mixed True on success, a WikiError object on failure.
1681 function sendConfirmationMail() {
1683 $url = $this->confirmationTokenUrl( $expiration );
1684 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
1685 wfMsg( 'confirmemail_body',
1689 $wgContLang->timeanddate( $expiration, false ) ) );
1693 * Send an e-mail to this user's account. Does not check for
1694 * confirmed status or validity.
1696 * @param string $subject
1697 * @param string $body
1698 * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
1699 * @return mixed True on success, a WikiError object on failure.
1701 function sendMail( $subject, $body, $from = null ) {
1702 if( is_null( $from ) ) {
1703 global $wgPasswordSender;
1704 $from = $wgPasswordSender;
1707 require_once( 'UserMailer.php' );
1708 $error = userMailer( $this->getEmail(), $from, $subject, $body );
1710 if( $error == '' ) {
1713 return new WikiError( $error );
1718 * Generate, store, and return a new e-mail confirmation code.
1719 * A hash (unsalted since it's used as a key) is stored.
1720 * @param &$expiration mixed output: accepts the expiration time
1724 function confirmationToken( &$expiration ) {
1725 $fname = 'User::confirmationToken';
1728 $expires = $now +
7 * 24 * 60 * 60;
1729 $expiration = wfTimestamp( TS_MW
, $expires );
1731 $token = $this->generateToken( $this->mId
. $this->mEmail
. $expires );
1732 $hash = md5( $token );
1734 $dbw =& wfGetDB( DB_MASTER
);
1735 $dbw->update( 'user',
1736 array( 'user_email_token' => $hash,
1737 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
1738 array( 'user_id' => $this->mId
),
1745 * Generate and store a new e-mail confirmation token, and return
1746 * the URL the user can use to confirm.
1747 * @param &$expiration mixed output: accepts the expiration time
1751 function confirmationTokenUrl( &$expiration ) {
1752 $token = $this->confirmationToken( $expiration );
1753 $title = Title
::makeTitle( NS_SPECIAL
, 'Confirmemail/' . $token );
1754 return $title->getFullUrl();
1758 * Mark the e-mail address confirmed and save.
1760 function confirmEmail() {
1761 $this->loadFromDatabase();
1762 $this->mEmailAuthenticated
= wfTimestampNow();
1763 $this->saveSettings();
1768 * Is this user allowed to send e-mails within limits of current
1769 * site configuration?
1772 function canSendEmail() {
1773 return $this->isEmailConfirmed();
1777 * Is this user allowed to receive e-mails within limits of current
1778 * site configuration?
1781 function canReceiveEmail() {
1782 return $this->canSendEmail() && !$this->getOption( 'disablemail' );
1786 * Is this user's e-mail address valid-looking and confirmed within
1787 * limits of the current site configuration?
1789 * If $wgEmailAuthentication is on, this may require the user to have
1790 * confirmed their address by returning a code or using a password
1791 * sent to the address from the wiki.
1795 function isEmailConfirmed() {
1796 global $wgEmailAuthentication;
1797 $this->loadFromDatabase();
1798 if( $this->isAnon() )
1800 if( !$this->isValidEmailAddr( $this->mEmail
) )
1802 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
1808 * @param array $groups list of groups
1809 * @return array list of permission key names for given groups combined
1812 function getGroupPermissions( $groups ) {
1813 global $wgGroupPermissions;
1815 foreach( $groups as $group ) {
1816 if( isset( $wgGroupPermissions[$group] ) ) {
1817 $rights = array_merge( $rights,
1818 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
1825 * @param string $group key name
1826 * @return string localized descriptive name, if provided
1829 function getGroupName( $group ) {
1830 $key = "group-$group-name";
1831 $name = wfMsg( $key );
1832 if( $name == '' ||
$name == "<$key>" ) {
1840 * Return the set of defined explicit groups.
1841 * The * and 'user' groups are not included.
1845 function getAllGroups() {
1846 global $wgGroupPermissions;
1848 array_keys( $wgGroupPermissions ),
1849 array( '*', 'user' ) );