a3464564eeb0fa3d5ff79d5e79decdb195c67014
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 * @todo Check for RFC 2822 compilance
226 * @param string $addr email address
230 function isValidEmailAddr ( $addr ) {
231 # There used to be a regular expression here, it got removed because it
232 # rejected valid addresses.
233 return ( trim( $addr ) != '' ) &&
234 (false !== strpos( $addr, '@' ) );
238 * Count the number of edits of a user
240 * @param int $uid The user ID to check
243 function edits( $uid ) {
244 $fname = 'User::edits';
246 $dbr =& wfGetDB( DB_SLAVE
);
247 return $dbr->selectField(
248 'revision', 'count(*)',
249 array( 'rev_user' => $uid ),
255 * probably return a random password
256 * @return string probably a random password
258 * @todo Check what is doing really [AV]
260 function randomPassword() {
261 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
262 $l = strlen( $pwchars ) - 1;
264 $np = $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
265 $pwchars{mt_rand( 0, $l )} . chr( mt_rand(48, 57) ) .
266 $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
267 $pwchars{mt_rand( 0, $l )};
272 * Set properties to default
273 * Used at construction. It will load per language default settings only
274 * if we have an available language object.
276 function loadDefaults() {
279 $fname = 'User::loadDefaults' . $n;
280 wfProfileIn( $fname );
282 global $wgContLang, $wgDBname;
283 global $wgNamespacesToBeSearchedDefault;
286 $this->mNewtalk
= -1;
287 $this->mName
= false;
288 $this->mRealName
= $this->mEmail
= '';
289 $this->mEmailAuthenticated
= null;
290 $this->mPassword
= $this->mNewpassword
= '';
291 $this->mRights
= array();
292 $this->mGroups
= array();
293 $this->mOptions
= User
::getDefaultOptions();
295 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
296 $this->mOptions
['searchNs'.$nsnum] = $val;
298 unset( $this->mSkin
);
299 $this->mDataLoaded
= false;
300 $this->mBlockedby
= -1; # Unset
301 $this->setToken(); # Random
302 $this->mHash
= false;
304 if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) {
305 $this->mTouched
= wfTimestamp( TS_MW
, $_COOKIE[$wgDBname.'LoggedOut'] );
308 $this->mTouched
= '0'; # Allow any pages to be cached
311 wfProfileOut( $fname );
315 * Combine the language default options with any site-specific options
316 * and add the default language variants.
322 function getDefaultOptions() {
324 * Site defaults will override the global/language defaults
326 global $wgContLang, $wgDefaultUserOptions;
327 $defOpt = $wgDefaultUserOptions +
$wgContLang->getDefaultUserOptions();
330 * default language setting
332 $variant = $wgContLang->getPreferredVariant();
333 $defOpt['variant'] = $variant;
334 $defOpt['language'] = $variant;
340 * Get a given default option value.
347 function getDefaultOption( $opt ) {
348 $defOpts = User
::getDefaultOptions();
349 if( isset( $defOpts[$opt] ) ) {
350 return $defOpts[$opt];
357 * Get blocking information
359 * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
360 * non-critical checks are done against slaves. Check when actually saving should be done against
363 * Note that even if $bFromSlave is false, the check is done first against slave, then master.
364 * The logic is that if blocked on slave, we'll assume it's either blocked on master or
365 * just slightly outta sync and soon corrected - safer to block slightly more that less.
366 * And it's cheaper to check slave first, then master if needed, than master always.
368 function getBlockedStatus( $bFromSlave = true ) {
369 global $wgBlockCache, $wgProxyList, $wgEnableSorbs, $wgProxyWhitelist;
371 if ( -1 != $this->mBlockedby
) {
372 wfDebug( "User::getBlockedStatus: already loaded.\n" );
376 $fname = 'User::getBlockedStatus';
377 wfProfileIn( $fname );
378 wfDebug( "$fname: checking...\n" );
380 $this->mBlockedby
= 0;
384 $block = new Block();
385 $block->forUpdate( $bFromSlave );
386 if ( $block->load( $ip , $this->mId
) ) {
387 wfDebug( "$fname: Found block.\n" );
388 $this->mBlockedby
= $block->mBy
;
389 $this->mBlockreason
= $block->mReason
;
390 if ( $this->isLoggedIn() ) {
391 $this->spreadBlock();
394 wfDebug( "$fname: No block.\n" );
398 if ( !$this->mBlockedby
) {
399 # Check first against slave, and optionally from master.
400 wfDebug( "$fname: Checking range blocks\n" );
401 $block = $wgBlockCache->get( $ip, true );
402 if ( !$block && !$bFromSlave )
404 # Not blocked: check against master, to make sure.
405 $wgBlockCache->clearLocal( );
406 $block = $wgBlockCache->get( $ip, false );
408 if ( $block !== false ) {
409 $this->mBlockedby
= $block->mBy
;
410 $this->mBlockreason
= $block->mReason
;
415 if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) {
418 if ( array_key_exists( $ip, $wgProxyList ) ) {
419 $this->mBlockedby
= wfMsg( 'proxyblocker' );
420 $this->mBlockreason
= wfMsg( 'proxyblockreason' );
424 if ( !$this->mBlockedby
&& $wgEnableSorbs && !$this->getID() ) {
425 if ( $this->inSorbsBlacklist( $ip ) ) {
426 $this->mBlockedby
= wfMsg( 'sorbs' );
427 $this->mBlockreason
= wfMsg( 'sorbsreason' );
433 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
435 wfProfileOut( $fname );
438 function inSorbsBlacklist( $ip ) {
439 global $wgEnableSorbs;
440 return $wgEnableSorbs &&
441 $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
444 function inOpmBlacklist( $ip ) {
446 return $wgEnableOpm &&
447 $this->inDnsBlacklist( $ip, 'opm.blitzed.org.' );
450 function inDnsBlacklist( $ip, $base ) {
451 $fname = 'User::inDnsBlacklist';
452 wfProfileIn( $fname );
457 if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
459 for ( $i=4; $i>=1; $i-- ) {
460 $host .= $m[$i] . '.';
465 $ipList = gethostbynamel( $host );
468 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
471 wfDebug( "Requested $host, not found in $base.\n" );
475 wfProfileOut( $fname );
480 * Primitive rate limits: enforce maximum actions per time period
481 * to put a brake on flooding.
483 * Note: when using a shared cache like memcached, IP-address
484 * last-hit counters will be shared across wikis.
486 * @return bool true if a rate limiter was tripped
489 function pingLimiter( $action='edit' ) {
490 global $wgRateLimits;
491 if( !isset( $wgRateLimits[$action] ) ) {
494 if( $this->isAllowed( 'delete' ) ) {
499 global $wgMemc, $wgDBname, $wgRateLimitLog;
500 $fname = 'User::pingLimiter';
501 wfProfileIn( $fname );
503 $limits = $wgRateLimits[$action];
505 $id = $this->getId();
508 if( isset( $limits['anon'] ) && $id == 0 ) {
509 $keys["$wgDBname:limiter:$action:anon"] = $limits['anon'];
512 if( isset( $limits['user'] ) && $id != 0 ) {
513 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['user'];
515 if( $this->isNewbie() ) {
516 if( isset( $limits['newbie'] ) && $id != 0 ) {
517 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['newbie'];
519 if( isset( $limits['ip'] ) ) {
520 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
522 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
523 $subnet = $matches[1];
524 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
529 foreach( $keys as $key => $limit ) {
530 list( $max, $period ) = $limit;
531 $summary = "(limit $max in {$period}s)";
532 $count = $wgMemc->get( $key );
534 if( $count > $max ) {
535 wfDebug( "$fname: tripped! $key at $count $summary\n" );
536 if( $wgRateLimitLog ) {
537 @error_log
( wfTimestamp( TS_MW
) . ' ' . $wgDBname . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
541 wfDebug( "$fname: ok. $key at $count $summary\n" );
544 wfDebug( "$fname: adding record for $key $summary\n" );
545 $wgMemc->add( $key, 1, intval( $period ) );
547 $wgMemc->incr( $key );
550 wfProfileOut( $fname );
555 * Check if user is blocked
556 * @return bool True if blocked, false otherwise
558 function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
559 wfDebug( "User::isBlocked: enter\n" );
560 $this->getBlockedStatus( $bFromSlave );
561 return $this->mBlockedby
!== 0;
565 * Check if user is blocked from editing a particular article
567 function isBlockedFrom( $title, $bFromSlave = false ) {
568 global $wgBlockAllowsUTEdit;
569 $fname = 'User::isBlockedFrom';
570 wfProfileIn( $fname );
571 wfDebug( "$fname: enter\n" );
573 if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
574 $title->getNamespace() == NS_USER_TALK
)
577 wfDebug( "$fname: self-talk page, ignoring any blocks\n" );
579 wfDebug( "$fname: asking isBlocked()\n" );
580 $blocked = $this->isBlocked( $bFromSlave );
582 wfProfileOut( $fname );
587 * Get name of blocker
588 * @return string name of blocker
590 function blockedBy() {
591 $this->getBlockedStatus();
592 return $this->mBlockedby
;
596 * Get blocking reason
597 * @return string Blocking reason
599 function blockedFor() {
600 $this->getBlockedStatus();
601 return $this->mBlockreason
;
605 * Initialise php session
607 function SetupSession() {
608 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
609 if( $wgSessionsInMemcached ) {
610 require_once( 'MemcachedSessions.php' );
611 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
612 # If it's left on 'user' or another setting from another
613 # application, it will end up failing. Try to recover.
614 ini_set ( 'session.save_handler', 'files' );
616 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
617 session_cache_limiter( 'private, must-revalidate' );
622 * Create a new user object using data from session
625 function loadFromSession() {
626 global $wgMemc, $wgDBname;
628 if ( isset( $_SESSION['wsUserID'] ) ) {
629 if ( 0 != $_SESSION['wsUserID'] ) {
630 $sId = $_SESSION['wsUserID'];
634 } else if ( isset( $_COOKIE["{$wgDBname}UserID"] ) ) {
635 $sId = intval( $_COOKIE["{$wgDBname}UserID"] );
636 $_SESSION['wsUserID'] = $sId;
640 if ( isset( $_SESSION['wsUserName'] ) ) {
641 $sName = $_SESSION['wsUserName'];
642 } else if ( isset( $_COOKIE["{$wgDBname}UserName"] ) ) {
643 $sName = $_COOKIE["{$wgDBname}UserName"];
644 $_SESSION['wsUserName'] = $sName;
649 $passwordCorrect = FALSE;
650 $user = $wgMemc->get( $key = "$wgDBname:user:id:$sId" );
651 if( !is_object( $user ) ||
$user->mVersion
< MW_USER_VERSION
) {
652 # Expire old serialized objects; they may be corrupt.
655 if($makenew = !$user) {
656 wfDebug( "User::loadFromSession() unable to load from memcached\n" );
659 $user->loadFromDatabase();
661 wfDebug( "User::loadFromSession() got from cache!\n" );
664 if ( isset( $_SESSION['wsToken'] ) ) {
665 $passwordCorrect = $_SESSION['wsToken'] == $user->mToken
;
666 } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) {
667 $passwordCorrect = $user->mToken
== $_COOKIE["{$wgDBname}Token"];
669 return new User(); # Can't log in from session
672 if ( ( $sName == $user->mName
) && $passwordCorrect ) {
674 if($wgMemc->set( $key, $user ))
675 wfDebug( "User::loadFromSession() successfully saved user\n" );
677 wfDebug( "User::loadFromSession() unable to save to memcached\n" );
681 return new User(); # Can't log in from session
685 * Load a user from the database
687 function loadFromDatabase() {
688 global $wgCommandLineMode;
689 $fname = "User::loadFromDatabase";
691 # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress
692 # loading in a command line script, don't assume all command line scripts need it like this
693 #if ( $this->mDataLoaded || $wgCommandLineMode ) {
694 if ( $this->mDataLoaded
) {
699 $this->mId
= intval( $this->mId
);
701 /** Anonymous user */
704 $this->mRights
= $this->getGroupPermissions( array( '*' ) );
705 $this->mDataLoaded
= true;
707 } # the following stuff is for non-anonymous users only
709 $dbr =& wfGetDB( DB_SLAVE
);
710 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
711 'user_email_authenticated',
712 'user_real_name','user_options','user_touched', 'user_token' ),
713 array( 'user_id' => $this->mId
), $fname );
715 if ( $s !== false ) {
716 $this->mName
= $s->user_name
;
717 $this->mEmail
= $s->user_email
;
718 $this->mEmailAuthenticated
= wfTimestampOrNull( TS_MW
, $s->user_email_authenticated
);
719 $this->mRealName
= $s->user_real_name
;
720 $this->mPassword
= $s->user_password
;
721 $this->mNewpassword
= $s->user_newpassword
;
722 $this->decodeOptions( $s->user_options
);
723 $this->mTouched
= wfTimestamp(TS_MW
,$s->user_touched
);
724 $this->mToken
= $s->user_token
;
726 $res = $dbr->select( 'user_groups',
728 array( 'ug_user' => $this->mId
),
730 $this->mGroups
= array();
731 while( $row = $dbr->fetchObject( $res ) ) {
732 $this->mGroups
[] = $row->ug_group
;
734 $effectiveGroups = array_merge( array( '*', 'user' ), $this->mGroups
);
735 $this->mRights
= $this->getGroupPermissions( $effectiveGroups );
738 $this->mDataLoaded
= true;
741 function getID() { return $this->mId
; }
742 function setID( $v ) {
744 $this->mDataLoaded
= false;
748 $this->loadFromDatabase();
749 if ( $this->mName
=== false ) {
750 $this->mName
= wfGetIP();
755 function setName( $str ) {
756 $this->loadFromDatabase();
762 * Return the title dbkey form of the name, for eg user pages.
766 function getTitleKey() {
767 return str_replace( ' ', '_', $this->getName() );
770 function getNewtalk() {
772 $fname = 'User::getNewtalk';
773 $this->loadFromDatabase();
775 # Load the newtalk status if it is unloaded (mNewtalk=-1)
776 if( $this->mNewtalk
== -1 ) {
777 $this->mNewtalk
= 0; # reset talk page status
779 # Check memcached separately for anons, who have no
780 # entire User object stored in there.
782 global $wgDBname, $wgMemc;
783 $key = "$wgDBname:newtalk:ip:" . $this->getName();
784 $newtalk = $wgMemc->get( $key );
785 if( is_integer( $newtalk ) ) {
786 $this->mNewtalk
= $newtalk ?
1 : 0;
787 return (bool)$this->mNewtalk
;
791 $dbr =& wfGetDB( DB_SLAVE
);
792 if ( $wgUseEnotif ) {
793 $res = $dbr->select( 'watchlist',
795 array( 'wl_title' => $this->getTitleKey(),
796 'wl_namespace' => NS_USER_TALK
,
797 'wl_user' => $this->mId
,
798 'wl_notificationtimestamp ' . $dbr->notNullTimestamp() ),
799 'User::getNewtalk' );
800 if( $dbr->numRows($res) > 0 ) {
803 $dbr->freeResult( $res );
804 } elseif ( $this->mId
) {
805 $res = $dbr->select( 'user_newtalk', 1, array( 'user_id' => $this->mId
), $fname );
807 if ( $dbr->numRows($res)>0 ) {
810 $dbr->freeResult( $res );
812 $res = $dbr->select( 'user_newtalk', 1, array( 'user_ip' => $this->getName() ), $fname );
813 $this->mNewtalk
= $dbr->numRows( $res ) > 0 ?
1 : 0;
814 $dbr->freeResult( $res );
818 $wgMemc->set( $key, $this->mNewtalk
, time() ); // + 1800 );
822 return ( 0 != $this->mNewtalk
);
825 function setNewtalk( $val ) {
826 $this->loadFromDatabase();
827 $this->mNewtalk
= $val;
828 $this->invalidateCache();
831 function invalidateCache() {
832 global $wgClockSkewFudge;
833 $this->loadFromDatabase();
834 $this->mTouched
= wfTimestamp(TS_MW
, time() +
$wgClockSkewFudge );
835 # Don't forget to save the options after this or
836 # it won't take effect!
839 function validateCache( $timestamp ) {
840 $this->loadFromDatabase();
841 return ($timestamp >= $this->mTouched
);
845 * Encrypt a password.
846 * It can eventuall salt a password @see User::addSalt()
847 * @param string $p clear Password.
848 * @return string Encrypted password.
850 function encryptPassword( $p ) {
851 return wfEncryptPassword( $this->mId
, $p );
854 # Set the password and reset the random token
855 function setPassword( $str ) {
856 $this->loadFromDatabase();
858 $this->mPassword
= $this->encryptPassword( $str );
859 $this->mNewpassword
= '';
862 # Set the random token (used for persistent authentication)
863 function setToken( $token = false ) {
864 global $wgSecretKey, $wgProxyKey, $wgDBname;
866 if ( $wgSecretKey ) {
868 } elseif ( $wgProxyKey ) {
873 $this->mToken
= md5( $key . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId
);
875 $this->mToken
= $token;
880 function setCookiePassword( $str ) {
881 $this->loadFromDatabase();
882 $this->mCookiePassword
= md5( $str );
885 function setNewpassword( $str ) {
886 $this->loadFromDatabase();
887 $this->mNewpassword
= $this->encryptPassword( $str );
890 function getEmail() {
891 $this->loadFromDatabase();
892 return $this->mEmail
;
895 function getEmailAuthenticationTimestamp() {
896 $this->loadFromDatabase();
897 return $this->mEmailAuthenticated
;
900 function setEmail( $str ) {
901 $this->loadFromDatabase();
902 $this->mEmail
= $str;
905 function getRealName() {
906 $this->loadFromDatabase();
907 return $this->mRealName
;
910 function setRealName( $str ) {
911 $this->loadFromDatabase();
912 $this->mRealName
= $str;
915 function getOption( $oname ) {
916 $this->loadFromDatabase();
917 if ( array_key_exists( $oname, $this->mOptions
) ) {
918 return trim( $this->mOptions
[$oname] );
924 function setOption( $oname, $val ) {
925 $this->loadFromDatabase();
926 if ( $oname == 'skin' ) {
927 # Clear cached skin, so the new one displays immediately in Special:Preferences
928 unset( $this->mSkin
);
930 $this->mOptions
[$oname] = $val;
931 $this->invalidateCache();
934 function getRights() {
935 $this->loadFromDatabase();
936 return $this->mRights
;
940 * Get the list of explicit group memberships this user has.
941 * The implicit * and user groups are not included.
942 * @return array of strings
944 function getGroups() {
945 $this->loadFromDatabase();
946 return $this->mGroups
;
950 * Get the list of implicit group memberships this user has.
951 * This includes all explicit groups, plus 'user' if logged in
952 * and '*' for all accounts.
953 * @return array of strings
955 function getEffectiveGroups() {
956 $base = array( '*' );
957 if( $this->isLoggedIn() ) {
960 return array_merge( $base, $this->getGroups() );
964 * Remove the user from the given group.
965 * This takes immediate effect.
968 function addGroup( $group ) {
969 $dbw =& wfGetDB( DB_MASTER
);
970 $dbw->insert( 'user_groups',
972 'ug_user' => $this->getID(),
973 'ug_group' => $group,
978 $this->mGroups
= array_merge( $this->mGroups
, array( $group ) );
979 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
981 $this->invalidateCache();
982 $this->saveSettings();
986 * Remove the user from the given group.
987 * This takes immediate effect.
990 function removeGroup( $group ) {
991 $dbw =& wfGetDB( DB_MASTER
);
992 $dbw->delete( 'user_groups',
994 'ug_user' => $this->getID(),
995 'ug_group' => $group,
997 'User::removeGroup' );
999 $this->mGroups
= array_diff( $this->mGroups
, array( $group ) );
1000 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1002 $this->invalidateCache();
1003 $this->saveSettings();
1008 * A more legible check for non-anonymousness.
1009 * Returns true if the user is not an anonymous visitor.
1013 function isLoggedIn() {
1014 return( $this->getID() != 0 );
1018 * A more legible check for anonymousness.
1019 * Returns true if the user is an anonymous visitor.
1024 return !$this->isLoggedIn();
1028 * Check if a user is sysop
1029 * Die with backtrace. Use User:isAllowed() instead.
1032 function isSysop() {
1033 return $this->isAllowed( 'protect' );
1037 function isDeveloper() {
1038 return $this->isAllowed( 'siteadmin' );
1042 function isBureaucrat() {
1043 return $this->isAllowed( 'makesysop' );
1047 * Whether the user is a bot
1048 * @todo need to be migrated to the new user level management sytem
1051 $this->loadFromDatabase();
1052 return in_array( 'bot', $this->mRights
);
1056 * Check if user is allowed to access a feature / make an action
1057 * @param string $action Action to be checked (see $wgAvailableRights in Defines.php for possible actions).
1058 * @return boolean True: action is allowed, False: action should not be allowed
1060 function isAllowed($action='') {
1061 $this->loadFromDatabase();
1062 return in_array( $action , $this->mRights
);
1066 * Load a skin if it doesn't exist or return it
1067 * @todo FIXME : need to check the old failback system [AV]
1069 function &getSkin() {
1070 global $IP, $wgRequest;
1071 if ( ! isset( $this->mSkin
) ) {
1072 $fname = 'User::getSkin';
1073 wfProfileIn( $fname );
1075 # get all skin names available
1076 $skinNames = Skin
::getSkinNames();
1079 $userSkin = $this->getOption( 'skin' );
1080 $userSkin = $wgRequest->getText('useskin', $userSkin);
1081 if ( $userSkin == '' ) { $userSkin = 'standard'; }
1083 if ( !isset( $skinNames[$userSkin] ) ) {
1084 # in case the user skin could not be found find a replacement
1088 2 => 'CologneBlue');
1089 # if phptal is enabled we should have monobook skin that
1090 # superseed the good old SkinStandard.
1091 if ( isset( $skinNames['monobook'] ) ) {
1092 $fallback[0] = 'MonoBook';
1095 if(is_numeric($userSkin) && isset( $fallback[$userSkin]) ){
1096 $sn = $fallback[$userSkin];
1101 # The user skin is available
1102 $sn = $skinNames[$userSkin];
1105 # Grab the skin class and initialise it. Each skin checks for PHPTal
1106 # and will not load if it's not enabled.
1107 require_once( $IP.'/skins/'.$sn.'.php' );
1109 # Check if we got if not failback to default skin
1110 $className = 'Skin'.$sn;
1111 if( !class_exists( $className ) ) {
1112 # DO NOT die if the class isn't found. This breaks maintenance
1113 # scripts and can cause a user account to be unrecoverable
1114 # except by SQL manipulation if a previously valid skin name
1115 # is no longer valid.
1116 $className = 'SkinStandard';
1117 require_once( $IP.'/skins/Standard.php' );
1119 $this->mSkin
=& new $className;
1120 wfProfileOut( $fname );
1122 return $this->mSkin
;
1126 * @param string $title Article title to look at
1130 * Check watched status of an article
1131 * @return bool True if article is watched
1133 function isWatched( $title ) {
1134 $wl = WatchedItem
::fromUserTitle( $this, $title );
1135 return $wl->isWatched();
1141 function addWatch( $title ) {
1142 $wl = WatchedItem
::fromUserTitle( $this, $title );
1144 $this->invalidateCache();
1148 * Stop watching an article
1150 function removeWatch( $title ) {
1151 $wl = WatchedItem
::fromUserTitle( $this, $title );
1153 $this->invalidateCache();
1157 * Clear the user's notification timestamp for the given title.
1158 * If e-notif e-mails are on, they will receive notification mails on
1159 * the next change of the page if it's watched etc.
1161 function clearNotification( &$title ) {
1162 global $wgUser, $wgUseEnotif;
1164 if ( !$wgUseEnotif ) {
1168 $userid = $this->getID();
1173 // Only update the timestamp if the page is being watched.
1174 // The query to find out if it is watched is cached both in memcached and per-invocation,
1175 // and when it does have to be executed, it can be on a slave
1176 // If this is the user's newtalk page, we always update the timestamp
1177 if ($title->getNamespace() == NS_USER_TALK
&&
1178 $title->getText() == $wgUser->getName())
1181 } elseif ( $this->getID() == $wgUser->getID() ) {
1182 $watched = $title->userIsWatching();
1187 // If the page is watched by the user (or may be watched), update the timestamp on any
1188 // any matching rows
1190 $dbw =& wfGetDB( DB_MASTER
);
1191 $success = $dbw->update( 'watchlist',
1193 'wl_notificationtimestamp' => NULL
1194 ), array( /* WHERE */
1195 'wl_title' => $title->getDBkey(),
1196 'wl_namespace' => $title->getNamespace(),
1197 'wl_user' => $this->getID()
1198 ), 'User::clearLastVisited'
1206 * Resets all of the given user's page-change notification timestamps.
1207 * If e-notif e-mails are on, they will receive notification mails on
1208 * the next change of any watched page.
1210 * @param int $currentUser user ID number
1213 function clearAllNotifications( $currentUser ) {
1214 global $wgUseEnotif;
1215 if ( !$wgUseEnotif ) {
1218 if( $currentUser != 0 ) {
1220 $dbw =& wfGetDB( DB_MASTER
);
1221 $success = $dbw->update( 'watchlist',
1223 'wl_notificationtimestamp' => 0
1224 ), array( /* WHERE */
1225 'wl_user' => $currentUser
1226 ), 'UserMailer::clearAll'
1229 # we also need to clear here the "you have new message" notification for the own user_talk page
1230 # This is cleared one page view later in Article::viewUpdates();
1236 * @return string Encoding options
1238 function encodeOptions() {
1240 foreach ( $this->mOptions
as $oname => $oval ) {
1241 array_push( $a, $oname.'='.$oval );
1243 $s = implode( "\n", $a );
1250 function decodeOptions( $str ) {
1251 $a = explode( "\n", $str );
1252 foreach ( $a as $s ) {
1253 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
1254 $this->mOptions
[$m[1]] = $m[2];
1259 function setCookies() {
1260 global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgDBname;
1261 if ( 0 == $this->mId
) return;
1262 $this->loadFromDatabase();
1263 $exp = time() +
$wgCookieExpiration;
1265 $_SESSION['wsUserID'] = $this->mId
;
1266 setcookie( $wgDBname.'UserID', $this->mId
, $exp, $wgCookiePath, $wgCookieDomain );
1268 $_SESSION['wsUserName'] = $this->getName();
1269 setcookie( $wgDBname.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain );
1271 $_SESSION['wsToken'] = $this->mToken
;
1272 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
1273 setcookie( $wgDBname.'Token', $this->mToken
, $exp, $wgCookiePath, $wgCookieDomain );
1275 setcookie( $wgDBname.'Token', '', time() - 3600 );
1281 * It will clean the session cookie
1284 global $wgCookiePath, $wgCookieDomain, $wgDBname;
1285 $this->loadDefaults();
1286 $this->setLoaded( true );
1288 $_SESSION['wsUserID'] = 0;
1290 setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1291 setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1293 # Remember when user logged out, to prevent seeing cached pages
1294 setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() +
86400, $wgCookiePath, $wgCookieDomain );
1298 * Save object settings into database
1300 function saveSettings() {
1301 global $wgMemc, $wgDBname, $wgUseEnotif;
1302 $fname = 'User::saveSettings';
1304 if ( wfReadOnly() ) { return; }
1305 $this->saveNewtalk();
1306 if ( 0 == $this->mId
) { return; }
1308 $dbw =& wfGetDB( DB_MASTER
);
1309 $dbw->update( 'user',
1311 'user_name' => $this->mName
,
1312 'user_password' => $this->mPassword
,
1313 'user_newpassword' => $this->mNewpassword
,
1314 'user_real_name' => $this->mRealName
,
1315 'user_email' => $this->mEmail
,
1316 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1317 'user_options' => $this->encodeOptions(),
1318 'user_touched' => $dbw->timestamp($this->mTouched
),
1319 'user_token' => $this->mToken
1320 ), array( /* WHERE */
1321 'user_id' => $this->mId
1324 $wgMemc->delete( "$wgDBname:user:id:$this->mId" );
1328 * Save value of new talk flag.
1330 function saveNewtalk() {
1331 global $wgDBname, $wgMemc, $wgUseEnotif;
1333 $fname = 'User::saveNewtalk';
1337 if ( wfReadOnly() ) { return ; }
1338 $dbr =& wfGetDB( DB_SLAVE
);
1339 $dbw =& wfGetDB( DB_MASTER
);
1341 if ( $wgUseEnotif ) {
1342 if ( ! $this->getNewtalk() ) {
1343 # Delete the watchlist entry for user_talk page X watched by user X
1344 $dbw->delete( 'watchlist',
1345 array( 'wl_user' => $this->mId
,
1346 'wl_title' => $this->getTitleKey(),
1347 'wl_namespace' => NS_USER_TALK
),
1349 if ( $dbw->affectedRows() ) {
1353 # Anon users have a separate memcache space for newtalk
1354 # since they don't store their own info. Trim...
1355 $wgMemc->delete( "$wgDBname:newtalk:ip:" . $this->getName() );
1359 if ($this->getID() != 0) {
1361 $value = $this->getID();
1365 $value = $this->getName();
1366 $key = "$wgDBname:newtalk:ip:$value";
1369 $dbr =& wfGetDB( DB_SLAVE
);
1370 $dbw =& wfGetDB( DB_MASTER
);
1372 $res = $dbr->selectField('user_newtalk', $field,
1373 array($field => $value), $fname);
1376 if ($res !== false && $this->mNewtalk
== 0) {
1377 $dbw->delete('user_newtalk', array($field => $value), $fname);
1379 $wgMemc->set( $key, 0 );
1381 } else if ($res === false && $this->mNewtalk
== 1) {
1382 $dbw->insert('user_newtalk', array($field => $value), $fname);
1384 $wgMemc->set( $key, 1 );
1391 # Update user_touched, so that newtalk notifications in the client cache are invalidated
1392 if ( $changed && $this->getID() ) {
1393 $dbw->update('user',
1394 /*SET*/ array( 'user_touched' => $this->mTouched
),
1395 /*WHERE*/ array( 'user_id' => $this->getID() ),
1397 $wgMemc->set( "$wgDBname:user:id:{$this->mId}", $this, 86400 );
1402 * Checks if a user with the given name exists, returns the ID
1404 function idForName() {
1405 $fname = 'User::idForName';
1408 $s = trim( $this->getName() );
1409 if ( 0 == strcmp( '', $s ) ) return 0;
1411 $dbr =& wfGetDB( DB_SLAVE
);
1412 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), $fname );
1413 if ( $id === false ) {
1420 * Add user object to the database
1422 function addToDatabase() {
1423 $fname = 'User::addToDatabase';
1424 $dbw =& wfGetDB( DB_MASTER
);
1425 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
1426 $dbw->insert( 'user',
1428 'user_id' => $seqVal,
1429 'user_name' => $this->mName
,
1430 'user_password' => $this->mPassword
,
1431 'user_newpassword' => $this->mNewpassword
,
1432 'user_email' => $this->mEmail
,
1433 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1434 'user_real_name' => $this->mRealName
,
1435 'user_options' => $this->encodeOptions(),
1436 'user_token' => $this->mToken
1439 $this->mId
= $dbw->insertId();
1442 function spreadBlock() {
1443 # If the (non-anonymous) user is blocked, this function will block any IP address
1444 # that they successfully log on from.
1445 $fname = 'User::spreadBlock';
1447 wfDebug( "User:spreadBlock()\n" );
1448 if ( $this->mId
== 0 ) {
1452 $userblock = Block
::newFromDB( '', $this->mId
);
1453 if ( !$userblock->isValid() ) {
1457 # Check if this IP address is already blocked
1458 $ipblock = Block
::newFromDB( wfGetIP() );
1459 if ( $ipblock->isValid() ) {
1460 # If the user is already blocked. Then check if the autoblock would
1461 # excede the user block. If it would excede, then do nothing, else
1462 # prolong block time
1463 if ($userblock->mExpiry
&&
1464 ($userblock->mExpiry
< Block
::getAutoblockExpiry($ipblock->mTimestamp
))) {
1467 # Just update the timestamp
1468 $ipblock->updateTimestamp();
1472 # Make a new block object with the desired properties
1473 wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
1474 $ipblock->mAddress
= wfGetIP();
1475 $ipblock->mUser
= 0;
1476 $ipblock->mBy
= $userblock->mBy
;
1477 $ipblock->mReason
= wfMsg( 'autoblocker', $this->getName(), $userblock->mReason
);
1478 $ipblock->mTimestamp
= wfTimestampNow();
1479 $ipblock->mAuto
= 1;
1480 # If the user is already blocked with an expiry date, we don't
1481 # want to pile on top of that!
1482 if($userblock->mExpiry
) {
1483 $ipblock->mExpiry
= min ( $userblock->mExpiry
, Block
::getAutoblockExpiry( $ipblock->mTimestamp
));
1485 $ipblock->mExpiry
= Block
::getAutoblockExpiry( $ipblock->mTimestamp
);
1493 function getPageRenderingHash() {
1496 return $this->mHash
;
1499 // stubthreshold is only included below for completeness,
1500 // it will always be 0 when this function is called by parsercache.
1502 $confstr = $this->getOption( 'math' );
1503 $confstr .= '!' . $this->getOption( 'stubthreshold' );
1504 $confstr .= '!' . $this->getOption( 'date' );
1505 $confstr .= '!' . $this->getOption( 'numberheadings' );
1506 $confstr .= '!' . $this->getOption( 'language' );
1507 $confstr .= '!' . $this->getOption( 'thumbsize' );
1508 // add in language specific options, if any
1509 $extra = $wgContLang->getExtraHashOptions();
1512 $this->mHash
= $confstr;
1516 function isAllowedToCreateAccount() {
1517 return $this->isAllowed( 'createaccount' ) && !$this->isBlocked();
1521 * Set mDataLoaded, return previous value
1522 * Use this to prevent DB access in command-line scripts or similar situations
1524 function setLoaded( $loaded ) {
1525 return wfSetVar( $this->mDataLoaded
, $loaded );
1529 * Get this user's personal page title.
1534 function getUserPage() {
1535 return Title
::makeTitle( NS_USER
, $this->getName() );
1539 * Get this user's talk page title.
1544 function getTalkPage() {
1545 $title = $this->getUserPage();
1546 return $title->getTalkPage();
1552 function getMaxID() {
1553 $dbr =& wfGetDB( DB_SLAVE
);
1554 return $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
1558 * Determine whether the user is a newbie. Newbies are either
1559 * anonymous IPs, or the 1% most recently created accounts.
1560 * Bots and sysops are excluded.
1561 * @return bool True if it is a newbie.
1563 function isNewbie() {
1564 return $this->isAnon() ||
$this->mId
> User
::getMaxID() * 0.99 && !$this->isAllowed( 'delete' ) && !$this->isBot();
1568 * Check to see if the given clear-text password is one of the accepted passwords
1569 * @param string $password User password.
1570 * @return bool True if the given password is correct otherwise False.
1572 function checkPassword( $password ) {
1573 global $wgAuth, $wgMinimalPasswordLength;
1574 $this->loadFromDatabase();
1576 // Even though we stop people from creating passwords that
1577 // are shorter than this, doesn't mean people wont be able
1578 // to. Certain authentication plugins do NOT want to save
1579 // domain passwords in a mysql database, so we should
1580 // check this (incase $wgAuth->strict() is false).
1581 if( strlen( $password ) < $wgMinimalPasswordLength ) {
1585 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
1587 } elseif( $wgAuth->strict() ) {
1588 /* Auth plugin doesn't allow local authentication */
1591 $ep = $this->encryptPassword( $password );
1592 if ( 0 == strcmp( $ep, $this->mPassword
) ) {
1594 } elseif ( ($this->mNewpassword
!= '') && (0 == strcmp( $ep, $this->mNewpassword
)) ) {
1596 } elseif ( function_exists( 'iconv' ) ) {
1597 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
1598 # Check for this with iconv
1599 $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
1600 if ( 0 == strcmp( $cp1252hash, $this->mPassword
) ) {
1608 * Initialize (if necessary) and return a session token value
1609 * which can be used in edit forms to show that the user's
1610 * login credentials aren't being hijacked with a foreign form
1613 * @param mixed $salt - Optional function-specific data for hash.
1614 * Use a string or an array of strings.
1618 function editToken( $salt = '' ) {
1619 if( !isset( $_SESSION['wsEditToken'] ) ) {
1620 $token = $this->generateToken();
1621 $_SESSION['wsEditToken'] = $token;
1623 $token = $_SESSION['wsEditToken'];
1625 if( is_array( $salt ) ) {
1626 $salt = implode( '|', $salt );
1628 return md5( $token . $salt );
1632 * Generate a hex-y looking random token for various uses.
1633 * Could be made more cryptographically sure if someone cares.
1636 function generateToken( $salt = '' ) {
1637 $token = dechex( mt_rand() ) . dechex( mt_rand() );
1638 return md5( $token . $salt );
1642 * Check given value against the token value stored in the session.
1643 * A match should confirm that the form was submitted from the
1644 * user's own login session, not a form submission from a third-party
1647 * @param string $val - the input value to compare
1648 * @param string $salt - Optional function-specific data for hash
1652 function matchEditToken( $val, $salt = '' ) {
1656 if ( !isset( $_SESSION['wsEditToken'] ) ) {
1657 $logfile = '/home/wikipedia/logs/session_debug/session.log';
1658 $mckey = memsess_key( session_id() );
1659 $uname = @posix_uname();
1660 $msg = "wsEditToken not set!\n" .
1661 'apache server=' . $uname['nodename'] . "\n" .
1662 'session_id = ' . session_id() . "\n" .
1663 '$_SESSION=' . var_export( $_SESSION, true ) . "\n" .
1664 '$_COOKIE=' . var_export( $_COOKIE, true ) . "\n" .
1665 "mc get($mckey) = " . var_export( $wgMemc->get( $mckey ), true ) . "\n\n\n";
1667 @error_log( $msg, 3, $logfile );
1670 return ( $val == $this->editToken( $salt ) );
1674 * Generate a new e-mail confirmation token and send a confirmation
1675 * mail to the user's given address.
1677 * @return mixed True on success, a WikiError object on failure.
1679 function sendConfirmationMail() {
1681 $url = $this->confirmationTokenUrl( $expiration );
1682 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
1683 wfMsg( 'confirmemail_body',
1687 $wgContLang->timeanddate( $expiration, false ) ) );
1691 * Send an e-mail to this user's account. Does not check for
1692 * confirmed status or validity.
1694 * @param string $subject
1695 * @param string $body
1696 * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
1697 * @return mixed True on success, a WikiError object on failure.
1699 function sendMail( $subject, $body, $from = null ) {
1700 if( is_null( $from ) ) {
1701 global $wgPasswordSender;
1702 $from = $wgPasswordSender;
1705 require_once( 'UserMailer.php' );
1706 $error = userMailer( $this->getEmail(), $from, $subject, $body );
1708 if( $error == '' ) {
1711 return new WikiError( $error );
1716 * Generate, store, and return a new e-mail confirmation code.
1717 * A hash (unsalted since it's used as a key) is stored.
1718 * @param &$expiration mixed output: accepts the expiration time
1722 function confirmationToken( &$expiration ) {
1723 $fname = 'User::confirmationToken';
1726 $expires = $now +
7 * 24 * 60 * 60;
1727 $expiration = wfTimestamp( TS_MW
, $expires );
1729 $token = $this->generateToken( $this->mId
. $this->mEmail
. $expires );
1730 $hash = md5( $token );
1732 $dbw =& wfGetDB( DB_MASTER
);
1733 $dbw->update( 'user',
1734 array( 'user_email_token' => $hash,
1735 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
1736 array( 'user_id' => $this->mId
),
1743 * Generate and store a new e-mail confirmation token, and return
1744 * the URL the user can use to confirm.
1745 * @param &$expiration mixed output: accepts the expiration time
1749 function confirmationTokenUrl( &$expiration ) {
1750 $token = $this->confirmationToken( $expiration );
1751 $title = Title
::makeTitle( NS_SPECIAL
, 'Confirmemail/' . $token );
1752 return $title->getFullUrl();
1756 * Mark the e-mail address confirmed and save.
1758 function confirmEmail() {
1759 $this->loadFromDatabase();
1760 $this->mEmailAuthenticated
= wfTimestampNow();
1761 $this->saveSettings();
1766 * Is this user allowed to send e-mails within limits of current
1767 * site configuration?
1770 function canSendEmail() {
1771 return $this->isEmailConfirmed();
1775 * Is this user allowed to receive e-mails within limits of current
1776 * site configuration?
1779 function canReceiveEmail() {
1780 return $this->canSendEmail() && !$this->getOption( 'disablemail' );
1784 * Is this user's e-mail address valid-looking and confirmed within
1785 * limits of the current site configuration?
1787 * If $wgEmailAuthentication is on, this may require the user to have
1788 * confirmed their address by returning a code or using a password
1789 * sent to the address from the wiki.
1793 function isEmailConfirmed() {
1794 global $wgEmailAuthentication;
1795 $this->loadFromDatabase();
1796 if( $this->isAnon() )
1798 if( !$this->isValidEmailAddr( $this->mEmail
) )
1800 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
1806 * @param array $groups list of groups
1807 * @return array list of permission key names for given groups combined
1810 function getGroupPermissions( $groups ) {
1811 global $wgGroupPermissions;
1813 foreach( $groups as $group ) {
1814 if( isset( $wgGroupPermissions[$group] ) ) {
1815 $rights = array_merge( $rights,
1816 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
1823 * @param string $group key name
1824 * @return string localized descriptive name, if provided
1827 function getGroupName( $group ) {
1828 $key = "group-$group-name";
1829 $name = wfMsg( $key );
1830 if( $name == '' ||
$name == "<$key>" ) {
1838 * Return the set of defined explicit groups.
1839 * The * and 'user' groups are not included.
1843 function getAllGroups() {
1844 global $wgGroupPermissions;
1846 array_keys( $wgGroupPermissions ),
1847 array( '*', 'user' ) );