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?
163 * @param string $name Nickname of a user
166 function isIP( $name ) {
167 return preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/",$name);
168 /*return preg_match("/^
169 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
170 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
171 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
172 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))
177 * Is the input a valid username?
179 * Checks if the input is a valid username, we don't want an empty string,
180 * an IP address, anything that containins slashes (would mess up subpages),
181 * is longer than the maximum allowed username size or doesn't begin with
184 * @param string $name
188 function isValidUserName( $name ) {
189 global $wgContLang, $wgMaxNameChars;
192 || User
::isIP( $name )
193 ||
strpos( $name, '/' ) !== false
194 ||
strlen( $name ) > $wgMaxNameChars
195 ||
$name != $wgContLang->ucfirst( $name ) )
202 * Is the input a valid password?
204 * @param string $password
208 function isValidPassword( $password ) {
209 global $wgMinimalPasswordLength;
210 return strlen( $password ) >= $wgMinimalPasswordLength;
214 * does the string match roughly an email address ?
216 * @todo Check for RFC 2822 compilance
219 * @param string $addr email address
223 function isValidEmailAddr ( $addr ) {
224 # There used to be a regular expression here, it got removed because it
225 # rejected valid addresses.
226 return ( trim( $addr ) != '' ) &&
227 (false !== strpos( $addr, '@' ) );
231 * Count the number of edits of a user
233 * @param int $uid The user ID to check
236 function edits( $uid ) {
237 $fname = 'User::edits';
239 $dbr =& wfGetDB( DB_SLAVE
);
240 return $dbr->selectField(
241 'revision', 'count(*)',
242 array( 'rev_user' => $uid ),
248 * probably return a random password
249 * @return string probably a random password
251 * @todo Check what is doing really [AV]
253 function randomPassword() {
254 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
255 $l = strlen( $pwchars ) - 1;
257 $np = $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
258 $pwchars{mt_rand( 0, $l )} . chr( mt_rand(48, 57) ) .
259 $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} .
260 $pwchars{mt_rand( 0, $l )};
265 * Set properties to default
266 * Used at construction. It will load per language default settings only
267 * if we have an available language object.
269 function loadDefaults() {
272 $fname = 'User::loadDefaults' . $n;
273 wfProfileIn( $fname );
275 global $wgContLang, $wgDBname;
276 global $wgNamespacesToBeSearchedDefault;
279 $this->mNewtalk
= -1;
280 $this->mName
= false;
281 $this->mRealName
= $this->mEmail
= '';
282 $this->mEmailAuthenticated
= null;
283 $this->mPassword
= $this->mNewpassword
= '';
284 $this->mRights
= array();
285 $this->mGroups
= array();
286 $this->mOptions
= User
::getDefaultOptions();
288 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
289 $this->mOptions
['searchNs'.$nsnum] = $val;
291 unset( $this->mSkin
);
292 $this->mDataLoaded
= false;
293 $this->mBlockedby
= -1; # Unset
294 $this->setToken(); # Random
295 $this->mHash
= false;
297 if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) {
298 $this->mTouched
= wfTimestamp( TS_MW
, $_COOKIE[$wgDBname.'LoggedOut'] );
301 $this->mTouched
= '0'; # Allow any pages to be cached
304 wfProfileOut( $fname );
308 * Combine the language default options with any site-specific options
309 * and add the default language variants.
315 function getDefaultOptions() {
317 * Site defaults will override the global/language defaults
319 global $wgContLang, $wgDefaultUserOptions;
320 $defOpt = $wgDefaultUserOptions +
$wgContLang->getDefaultUserOptions();
323 * default language setting
325 $variant = $wgContLang->getPreferredVariant();
326 $defOpt['variant'] = $variant;
327 $defOpt['language'] = $variant;
333 * Get a given default option value.
340 function getDefaultOption( $opt ) {
341 $defOpts = User
::getDefaultOptions();
342 if( isset( $defOpts[$opt] ) ) {
343 return $defOpts[$opt];
350 * Get blocking information
352 * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
353 * non-critical checks are done against slaves. Check when actually saving should be done against
356 * Note that even if $bFromSlave is false, the check is done first against slave, then master.
357 * The logic is that if blocked on slave, we'll assume it's either blocked on master or
358 * just slightly outta sync and soon corrected - safer to block slightly more that less.
359 * And it's cheaper to check slave first, then master if needed, than master always.
361 function getBlockedStatus( $bFromSlave = true ) {
362 global $wgBlockCache, $wgProxyList, $wgEnableSorbs, $wgProxyWhitelist;
364 if ( -1 != $this->mBlockedby
) {
365 wfDebug( "User::getBlockedStatus: already loaded.\n" );
369 $fname = 'User::getBlockedStatus';
370 wfProfileIn( $fname );
371 wfDebug( "$fname: checking...\n" );
373 $this->mBlockedby
= 0;
377 $block = new Block();
378 $block->forUpdate( $bFromSlave );
379 if ( $block->load( $ip , $this->mId
) ) {
380 wfDebug( "$fname: Found block.\n" );
381 $this->mBlockedby
= $block->mBy
;
382 $this->mBlockreason
= $block->mReason
;
383 if ( $this->isLoggedIn() ) {
384 $this->spreadBlock();
387 wfDebug( "$fname: No block.\n" );
391 if ( !$this->mBlockedby
) {
392 # Check first against slave, and optionally from master.
393 wfDebug( "$fname: Checking range blocks\n" );
394 $block = $wgBlockCache->get( $ip, true );
395 if ( !$block && !$bFromSlave )
397 # Not blocked: check against master, to make sure.
398 $wgBlockCache->clearLocal( );
399 $block = $wgBlockCache->get( $ip, false );
401 if ( $block !== false ) {
402 $this->mBlockedby
= $block->mBy
;
403 $this->mBlockreason
= $block->mReason
;
408 if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) {
411 if ( array_key_exists( $ip, $wgProxyList ) ) {
412 $this->mBlockedby
= wfMsg( 'proxyblocker' );
413 $this->mBlockreason
= wfMsg( 'proxyblockreason' );
417 if ( !$this->mBlockedby
&& $wgEnableSorbs && !$this->getID() ) {
418 if ( $this->inSorbsBlacklist( $ip ) ) {
419 $this->mBlockedby
= wfMsg( 'sorbs' );
420 $this->mBlockreason
= wfMsg( 'sorbsreason' );
424 wfProfileOut( $fname );
427 function inSorbsBlacklist( $ip ) {
428 global $wgEnableSorbs;
429 return $wgEnableSorbs &&
430 $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
433 function inOpmBlacklist( $ip ) {
435 return $wgEnableOpm &&
436 $this->inDnsBlacklist( $ip, 'opm.blitzed.org.' );
439 function inDnsBlacklist( $ip, $base ) {
440 $fname = 'User::inDnsBlacklist';
441 wfProfileIn( $fname );
446 if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
448 for ( $i=4; $i>=1; $i-- ) {
449 $host .= $m[$i] . '.';
454 $ipList = gethostbynamel( $host );
457 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
460 wfDebug( "Requested $host, not found in $base.\n" );
464 wfProfileOut( $fname );
469 * Primitive rate limits: enforce maximum actions per time period
470 * to put a brake on flooding.
472 * Note: when using a shared cache like memcached, IP-address
473 * last-hit counters will be shared across wikis.
475 * @return bool true if a rate limiter was tripped
478 function pingLimiter( $action='edit' ) {
479 global $wgRateLimits;
480 if( !isset( $wgRateLimits[$action] ) ) {
483 if( $this->isAllowed( 'delete' ) ) {
488 global $wgMemc, $wgDBname, $wgRateLimitLog;
489 $fname = 'User::pingLimiter';
490 wfProfileIn( $fname );
492 $limits = $wgRateLimits[$action];
494 $id = $this->getId();
497 if( isset( $limits['anon'] ) && $id == 0 ) {
498 $keys["$wgDBname:limiter:$action:anon"] = $limits['anon'];
501 if( isset( $limits['user'] ) && $id != 0 ) {
502 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['user'];
504 if( $this->isNewbie() ) {
505 if( isset( $limits['newbie'] ) && $id != 0 ) {
506 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['newbie'];
508 if( isset( $limits['ip'] ) ) {
509 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
511 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
512 $subnet = $matches[1];
513 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
518 foreach( $keys as $key => $limit ) {
519 list( $max, $period ) = $limit;
520 $summary = "(limit $max in {$period}s)";
521 $count = $wgMemc->get( $key );
523 if( $count > $max ) {
524 wfDebug( "$fname: tripped! $key at $count $summary\n" );
525 if( $wgRateLimitLog ) {
526 @error_log
( wfTimestamp( TS_MW
) . ' ' . $wgDBname . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
530 wfDebug( "$fname: ok. $key at $count $summary\n" );
533 wfDebug( "$fname: adding record for $key $summary\n" );
534 $wgMemc->add( $key, 1, intval( $period ) );
536 $wgMemc->incr( $key );
539 wfProfileOut( $fname );
544 * Check if user is blocked
545 * @return bool True if blocked, false otherwise
547 function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
548 wfDebug( "User::isBlocked: enter\n" );
549 $this->getBlockedStatus( $bFromSlave );
550 return $this->mBlockedby
!== 0;
554 * Check if user is blocked from editing a particular article
556 function isBlockedFrom( $title, $bFromSlave = false ) {
557 global $wgBlockAllowsUTEdit;
558 $fname = 'User::isBlockedFrom';
559 wfProfileIn( $fname );
560 wfDebug( "$fname: enter\n" );
562 if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
563 $title->getNamespace() == NS_USER_TALK
)
566 wfDebug( "$fname: self-talk page, ignoring any blocks\n" );
568 wfDebug( "$fname: asking isBlocked()\n" );
569 $blocked = $this->isBlocked( $bFromSlave );
571 wfProfileOut( $fname );
576 * Get name of blocker
577 * @return string name of blocker
579 function blockedBy() {
580 $this->getBlockedStatus();
581 return $this->mBlockedby
;
585 * Get blocking reason
586 * @return string Blocking reason
588 function blockedFor() {
589 $this->getBlockedStatus();
590 return $this->mBlockreason
;
594 * Initialise php session
596 function SetupSession() {
597 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
598 if( $wgSessionsInMemcached ) {
599 require_once( 'MemcachedSessions.php' );
600 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
601 # If it's left on 'user' or another setting from another
602 # application, it will end up failing. Try to recover.
603 ini_set ( 'session.save_handler', 'files' );
605 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
606 session_cache_limiter( 'private, must-revalidate' );
611 * Create a new user object using data from session
614 function loadFromSession() {
615 global $wgMemc, $wgDBname;
617 if ( isset( $_SESSION['wsUserID'] ) ) {
618 if ( 0 != $_SESSION['wsUserID'] ) {
619 $sId = $_SESSION['wsUserID'];
623 } else if ( isset( $_COOKIE["{$wgDBname}UserID"] ) ) {
624 $sId = intval( $_COOKIE["{$wgDBname}UserID"] );
625 $_SESSION['wsUserID'] = $sId;
629 if ( isset( $_SESSION['wsUserName'] ) ) {
630 $sName = $_SESSION['wsUserName'];
631 } else if ( isset( $_COOKIE["{$wgDBname}UserName"] ) ) {
632 $sName = $_COOKIE["{$wgDBname}UserName"];
633 $_SESSION['wsUserName'] = $sName;
638 $passwordCorrect = FALSE;
639 $user = $wgMemc->get( $key = "$wgDBname:user:id:$sId" );
640 if( !is_object( $user ) ||
$user->mVersion
< MW_USER_VERSION
) {
641 # Expire old serialized objects; they may be corrupt.
644 if($makenew = !$user) {
645 wfDebug( "User::loadFromSession() unable to load from memcached\n" );
648 $user->loadFromDatabase();
650 wfDebug( "User::loadFromSession() got from cache!\n" );
653 if ( isset( $_SESSION['wsToken'] ) ) {
654 $passwordCorrect = $_SESSION['wsToken'] == $user->mToken
;
655 } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) {
656 $passwordCorrect = $user->mToken
== $_COOKIE["{$wgDBname}Token"];
658 return new User(); # Can't log in from session
661 if ( ( $sName == $user->mName
) && $passwordCorrect ) {
663 if($wgMemc->set( $key, $user ))
664 wfDebug( "User::loadFromSession() successfully saved user\n" );
666 wfDebug( "User::loadFromSession() unable to save to memcached\n" );
670 return new User(); # Can't log in from session
674 * Load a user from the database
676 function loadFromDatabase() {
677 global $wgCommandLineMode;
678 $fname = "User::loadFromDatabase";
680 # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress
681 # loading in a command line script, don't assume all command line scripts need it like this
682 #if ( $this->mDataLoaded || $wgCommandLineMode ) {
683 if ( $this->mDataLoaded
) {
688 $this->mId
= intval( $this->mId
);
690 /** Anonymous user */
693 $this->mRights
= $this->getGroupPermissions( array( '*' ) );
694 $this->mDataLoaded
= true;
696 } # the following stuff is for non-anonymous users only
698 $dbr =& wfGetDB( DB_SLAVE
);
699 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
700 'user_email_authenticated',
701 'user_real_name','user_options','user_touched', 'user_token' ),
702 array( 'user_id' => $this->mId
), $fname );
704 if ( $s !== false ) {
705 $this->mName
= $s->user_name
;
706 $this->mEmail
= $s->user_email
;
707 $this->mEmailAuthenticated
= wfTimestampOrNull( TS_MW
, $s->user_email_authenticated
);
708 $this->mRealName
= $s->user_real_name
;
709 $this->mPassword
= $s->user_password
;
710 $this->mNewpassword
= $s->user_newpassword
;
711 $this->decodeOptions( $s->user_options
);
712 $this->mTouched
= wfTimestamp(TS_MW
,$s->user_touched
);
713 $this->mToken
= $s->user_token
;
715 $res = $dbr->select( 'user_groups',
717 array( 'ug_user' => $this->mId
),
719 $this->mGroups
= array();
720 while( $row = $dbr->fetchObject( $res ) ) {
721 $this->mGroups
[] = $row->ug_group
;
723 $effectiveGroups = array_merge( array( '*', 'user' ), $this->mGroups
);
724 $this->mRights
= $this->getGroupPermissions( $effectiveGroups );
727 $this->mDataLoaded
= true;
730 function getID() { return $this->mId
; }
731 function setID( $v ) {
733 $this->mDataLoaded
= false;
737 $this->loadFromDatabase();
738 if ( $this->mName
=== false ) {
739 $this->mName
= wfGetIP();
744 function setName( $str ) {
745 $this->loadFromDatabase();
751 * Return the title dbkey form of the name, for eg user pages.
755 function getTitleKey() {
756 return str_replace( ' ', '_', $this->getName() );
759 function getNewtalk() {
761 $fname = 'User::getNewtalk';
762 $this->loadFromDatabase();
764 # Load the newtalk status if it is unloaded (mNewtalk=-1)
765 if( $this->mNewtalk
== -1 ) {
766 $this->mNewtalk
= 0; # reset talk page status
768 # Check memcached separately for anons, who have no
769 # entire User object stored in there.
771 global $wgDBname, $wgMemc;
772 $key = "$wgDBname:newtalk:ip:" . $this->getName();
773 $newtalk = $wgMemc->get( $key );
774 if( is_integer( $newtalk ) ) {
775 $this->mNewtalk
= $newtalk ?
1 : 0;
776 return (bool)$this->mNewtalk
;
780 $dbr =& wfGetDB( DB_SLAVE
);
781 if ( $wgUseEnotif ) {
782 $res = $dbr->select( 'watchlist',
784 array( 'wl_title' => $this->getTitleKey(),
785 'wl_namespace' => NS_USER_TALK
,
786 'wl_user' => $this->mId
,
787 'wl_notificationtimestamp ' . $dbr->notNullTimestamp() ),
788 'User::getNewtalk' );
789 if( $dbr->numRows($res) > 0 ) {
792 $dbr->freeResult( $res );
793 } elseif ( $this->mId
) {
794 $res = $dbr->select( 'user_newtalk', 1, array( 'user_id' => $this->mId
), $fname );
796 if ( $dbr->numRows($res)>0 ) {
799 $dbr->freeResult( $res );
801 $res = $dbr->select( 'user_newtalk', 1, array( 'user_ip' => $this->getName() ), $fname );
802 $this->mNewtalk
= $dbr->numRows( $res ) > 0 ?
1 : 0;
803 $dbr->freeResult( $res );
807 $wgMemc->set( $key, $this->mNewtalk
, time() ); // + 1800 );
811 return ( 0 != $this->mNewtalk
);
814 function setNewtalk( $val ) {
815 $this->loadFromDatabase();
816 $this->mNewtalk
= $val;
817 $this->invalidateCache();
820 function invalidateCache() {
821 global $wgClockSkewFudge;
822 $this->loadFromDatabase();
823 $this->mTouched
= wfTimestamp(TS_MW
, time() +
$wgClockSkewFudge );
824 # Don't forget to save the options after this or
825 # it won't take effect!
828 function validateCache( $timestamp ) {
829 $this->loadFromDatabase();
830 return ($timestamp >= $this->mTouched
);
834 * Encrypt a password.
835 * It can eventuall salt a password @see User::addSalt()
836 * @param string $p clear Password.
837 * @return string Encrypted password.
839 function encryptPassword( $p ) {
840 return wfEncryptPassword( $this->mId
, $p );
843 # Set the password and reset the random token
844 function setPassword( $str ) {
845 $this->loadFromDatabase();
847 $this->mPassword
= $this->encryptPassword( $str );
848 $this->mNewpassword
= '';
851 # Set the random token (used for persistent authentication)
852 function setToken( $token = false ) {
853 global $wgSecretKey, $wgProxyKey, $wgDBname;
855 if ( $wgSecretKey ) {
857 } elseif ( $wgProxyKey ) {
862 $this->mToken
= md5( $key . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId
);
864 $this->mToken
= $token;
869 function setCookiePassword( $str ) {
870 $this->loadFromDatabase();
871 $this->mCookiePassword
= md5( $str );
874 function setNewpassword( $str ) {
875 $this->loadFromDatabase();
876 $this->mNewpassword
= $this->encryptPassword( $str );
879 function getEmail() {
880 $this->loadFromDatabase();
881 return $this->mEmail
;
884 function getEmailAuthenticationTimestamp() {
885 $this->loadFromDatabase();
886 return $this->mEmailAuthenticated
;
889 function setEmail( $str ) {
890 $this->loadFromDatabase();
891 $this->mEmail
= $str;
894 function getRealName() {
895 $this->loadFromDatabase();
896 return $this->mRealName
;
899 function setRealName( $str ) {
900 $this->loadFromDatabase();
901 $this->mRealName
= $str;
904 function getOption( $oname ) {
905 $this->loadFromDatabase();
906 if ( array_key_exists( $oname, $this->mOptions
) ) {
907 return trim( $this->mOptions
[$oname] );
913 function setOption( $oname, $val ) {
914 $this->loadFromDatabase();
915 if ( $oname == 'skin' ) {
916 # Clear cached skin, so the new one displays immediately in Special:Preferences
917 unset( $this->mSkin
);
919 $this->mOptions
[$oname] = $val;
920 $this->invalidateCache();
923 function getRights() {
924 $this->loadFromDatabase();
925 return $this->mRights
;
929 * Get the list of explicit group memberships this user has.
930 * The implicit * and user groups are not included.
931 * @return array of strings
933 function getGroups() {
934 $this->loadFromDatabase();
935 return $this->mGroups
;
939 * Get the list of implicit group memberships this user has.
940 * This includes all explicit groups, plus 'user' if logged in
941 * and '*' for all accounts.
942 * @return array of strings
944 function getEffectiveGroups() {
945 $base = array( '*' );
946 if( $this->isLoggedIn() ) {
949 return array_merge( $base, $this->getGroups() );
953 * Remove the user from the given group.
954 * This takes immediate effect.
957 function addGroup( $group ) {
958 $dbw =& wfGetDB( DB_MASTER
);
959 $dbw->insert( 'user_groups',
961 'ug_user' => $this->getID(),
962 'ug_group' => $group,
967 $this->mGroups
= array_merge( $this->mGroups
, array( $group ) );
968 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
970 $this->invalidateCache();
971 $this->saveSettings();
975 * Remove the user from the given group.
976 * This takes immediate effect.
979 function removeGroup( $group ) {
980 $dbw =& wfGetDB( DB_MASTER
);
981 $dbw->delete( 'user_groups',
983 'ug_user' => $this->getID(),
984 'ug_group' => $group,
986 'User::removeGroup' );
988 $this->mGroups
= array_diff( $this->mGroups
, array( $group ) );
989 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
991 $this->invalidateCache();
992 $this->saveSettings();
997 * A more legible check for non-anonymousness.
998 * Returns true if the user is not an anonymous visitor.
1002 function isLoggedIn() {
1003 return( $this->getID() != 0 );
1007 * A more legible check for anonymousness.
1008 * Returns true if the user is an anonymous visitor.
1013 return !$this->isLoggedIn();
1017 * Check if a user is sysop
1018 * Die with backtrace. Use User:isAllowed() instead.
1021 function isSysop() {
1022 return $this->isAllowed( 'protect' );
1026 function isDeveloper() {
1027 return $this->isAllowed( 'siteadmin' );
1031 function isBureaucrat() {
1032 return $this->isAllowed( 'makesysop' );
1036 * Whether the user is a bot
1037 * @todo need to be migrated to the new user level management sytem
1040 $this->loadFromDatabase();
1041 return in_array( 'bot', $this->mRights
);
1045 * Check if user is allowed to access a feature / make an action
1046 * @param string $action Action to be checked (see $wgAvailableRights in Defines.php for possible actions).
1047 * @return boolean True: action is allowed, False: action should not be allowed
1049 function isAllowed($action='') {
1050 $this->loadFromDatabase();
1051 return in_array( $action , $this->mRights
);
1055 * Load a skin if it doesn't exist or return it
1056 * @todo FIXME : need to check the old failback system [AV]
1058 function &getSkin() {
1059 global $IP, $wgRequest;
1060 if ( ! isset( $this->mSkin
) ) {
1061 $fname = 'User::getSkin';
1062 wfProfileIn( $fname );
1064 # get all skin names available
1065 $skinNames = Skin
::getSkinNames();
1068 $userSkin = $this->getOption( 'skin' );
1069 $userSkin = $wgRequest->getText('useskin', $userSkin);
1070 if ( $userSkin == '' ) { $userSkin = 'standard'; }
1072 if ( !isset( $skinNames[$userSkin] ) ) {
1073 # in case the user skin could not be found find a replacement
1077 2 => 'CologneBlue');
1078 # if phptal is enabled we should have monobook skin that
1079 # superseed the good old SkinStandard.
1080 if ( isset( $skinNames['monobook'] ) ) {
1081 $fallback[0] = 'MonoBook';
1084 if(is_numeric($userSkin) && isset( $fallback[$userSkin]) ){
1085 $sn = $fallback[$userSkin];
1090 # The user skin is available
1091 $sn = $skinNames[$userSkin];
1094 # Grab the skin class and initialise it. Each skin checks for PHPTal
1095 # and will not load if it's not enabled.
1096 require_once( $IP.'/skins/'.$sn.'.php' );
1098 # Check if we got if not failback to default skin
1099 $className = 'Skin'.$sn;
1100 if( !class_exists( $className ) ) {
1101 # DO NOT die if the class isn't found. This breaks maintenance
1102 # scripts and can cause a user account to be unrecoverable
1103 # except by SQL manipulation if a previously valid skin name
1104 # is no longer valid.
1105 $className = 'SkinStandard';
1106 require_once( $IP.'/skins/Standard.php' );
1108 $this->mSkin
=& new $className;
1109 wfProfileOut( $fname );
1111 return $this->mSkin
;
1115 * @param string $title Article title to look at
1119 * Check watched status of an article
1120 * @return bool True if article is watched
1122 function isWatched( $title ) {
1123 $wl = WatchedItem
::fromUserTitle( $this, $title );
1124 return $wl->isWatched();
1130 function addWatch( $title ) {
1131 $wl = WatchedItem
::fromUserTitle( $this, $title );
1133 $this->invalidateCache();
1137 * Stop watching an article
1139 function removeWatch( $title ) {
1140 $wl = WatchedItem
::fromUserTitle( $this, $title );
1142 $this->invalidateCache();
1146 * Clear the user's notification timestamp for the given title.
1147 * If e-notif e-mails are on, they will receive notification mails on
1148 * the next change of the page if it's watched etc.
1150 function clearNotification( &$title ) {
1151 global $wgUser, $wgUseEnotif;
1153 if ( !$wgUseEnotif ) {
1157 $userid = $this->getID();
1162 // Only update the timestamp if the page is being watched.
1163 // The query to find out if it is watched is cached both in memcached and per-invocation,
1164 // and when it does have to be executed, it can be on a slave
1165 // If this is the user's newtalk page, we always update the timestamp
1166 if ($title->getNamespace() == NS_USER_TALK
&&
1167 $title->getText() == $wgUser->getName())
1170 } elseif ( $this->getID() == $wgUser->getID() ) {
1171 $watched = $title->userIsWatching();
1176 // If the page is watched by the user (or may be watched), update the timestamp on any
1177 // any matching rows
1179 $dbw =& wfGetDB( DB_MASTER
);
1180 $success = $dbw->update( 'watchlist',
1182 'wl_notificationtimestamp' => NULL
1183 ), array( /* WHERE */
1184 'wl_title' => $title->getDBkey(),
1185 'wl_namespace' => $title->getNamespace(),
1186 'wl_user' => $this->getID()
1187 ), 'User::clearLastVisited'
1195 * Resets all of the given user's page-change notification timestamps.
1196 * If e-notif e-mails are on, they will receive notification mails on
1197 * the next change of any watched page.
1199 * @param int $currentUser user ID number
1202 function clearAllNotifications( $currentUser ) {
1203 global $wgUseEnotif;
1204 if ( !$wgUseEnotif ) {
1207 if( $currentUser != 0 ) {
1209 $dbw =& wfGetDB( DB_MASTER
);
1210 $success = $dbw->update( 'watchlist',
1212 'wl_notificationtimestamp' => 0
1213 ), array( /* WHERE */
1214 'wl_user' => $currentUser
1215 ), 'UserMailer::clearAll'
1218 # we also need to clear here the "you have new message" notification for the own user_talk page
1219 # This is cleared one page view later in Article::viewUpdates();
1225 * @return string Encoding options
1227 function encodeOptions() {
1229 foreach ( $this->mOptions
as $oname => $oval ) {
1230 array_push( $a, $oname.'='.$oval );
1232 $s = implode( "\n", $a );
1239 function decodeOptions( $str ) {
1240 $a = explode( "\n", $str );
1241 foreach ( $a as $s ) {
1242 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
1243 $this->mOptions
[$m[1]] = $m[2];
1248 function setCookies() {
1249 global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgDBname;
1250 if ( 0 == $this->mId
) return;
1251 $this->loadFromDatabase();
1252 $exp = time() +
$wgCookieExpiration;
1254 $_SESSION['wsUserID'] = $this->mId
;
1255 setcookie( $wgDBname.'UserID', $this->mId
, $exp, $wgCookiePath, $wgCookieDomain );
1257 $_SESSION['wsUserName'] = $this->getName();
1258 setcookie( $wgDBname.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain );
1260 $_SESSION['wsToken'] = $this->mToken
;
1261 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
1262 setcookie( $wgDBname.'Token', $this->mToken
, $exp, $wgCookiePath, $wgCookieDomain );
1264 setcookie( $wgDBname.'Token', '', time() - 3600 );
1270 * It will clean the session cookie
1273 global $wgCookiePath, $wgCookieDomain, $wgDBname;
1274 $this->loadDefaults();
1275 $this->setLoaded( true );
1277 $_SESSION['wsUserID'] = 0;
1279 setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1280 setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
1282 # Remember when user logged out, to prevent seeing cached pages
1283 setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() +
86400, $wgCookiePath, $wgCookieDomain );
1287 * Save object settings into database
1289 function saveSettings() {
1290 global $wgMemc, $wgDBname, $wgUseEnotif;
1291 $fname = 'User::saveSettings';
1293 if ( wfReadOnly() ) { return; }
1294 $this->saveNewtalk();
1295 if ( 0 == $this->mId
) { return; }
1297 $dbw =& wfGetDB( DB_MASTER
);
1298 $dbw->update( 'user',
1300 'user_name' => $this->mName
,
1301 'user_password' => $this->mPassword
,
1302 'user_newpassword' => $this->mNewpassword
,
1303 'user_real_name' => $this->mRealName
,
1304 'user_email' => $this->mEmail
,
1305 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1306 'user_options' => $this->encodeOptions(),
1307 'user_touched' => $dbw->timestamp($this->mTouched
),
1308 'user_token' => $this->mToken
1309 ), array( /* WHERE */
1310 'user_id' => $this->mId
1313 $wgMemc->delete( "$wgDBname:user:id:$this->mId" );
1317 * Save value of new talk flag.
1319 function saveNewtalk() {
1320 global $wgDBname, $wgMemc, $wgUseEnotif;
1322 $fname = 'User::saveNewtalk';
1326 if ( wfReadOnly() ) { return ; }
1327 $dbr =& wfGetDB( DB_SLAVE
);
1328 $dbw =& wfGetDB( DB_MASTER
);
1330 if ( $wgUseEnotif ) {
1331 if ( ! $this->getNewtalk() ) {
1332 # Delete the watchlist entry for user_talk page X watched by user X
1333 $dbw->delete( 'watchlist',
1334 array( 'wl_user' => $this->mId
,
1335 'wl_title' => $this->getTitleKey(),
1336 'wl_namespace' => NS_USER_TALK
),
1338 if ( $dbw->affectedRows() ) {
1342 # Anon users have a separate memcache space for newtalk
1343 # since they don't store their own info. Trim...
1344 $wgMemc->delete( "$wgDBname:newtalk:ip:" . $this->getName() );
1348 if ($this->getID() != 0) {
1350 $value = $this->getID();
1354 $value = $this->getName();
1355 $key = "$wgDBname:newtalk:ip:$value";
1358 $dbr =& wfGetDB( DB_SLAVE
);
1359 $dbw =& wfGetDB( DB_MASTER
);
1361 $res = $dbr->selectField('user_newtalk', $field,
1362 array($field => $value), $fname);
1365 if ($res !== false && $this->mNewtalk
== 0) {
1366 $dbw->delete('user_newtalk', array($field => $value), $fname);
1368 $wgMemc->set( $key, 0 );
1370 } else if ($res === false && $this->mNewtalk
== 1) {
1371 $dbw->insert('user_newtalk', array($field => $value), $fname);
1373 $wgMemc->set( $key, 1 );
1380 # Update user_touched, so that newtalk notifications in the client cache are invalidated
1381 if ( $changed && $this->getID() ) {
1382 $dbw->update('user',
1383 /*SET*/ array( 'user_touched' => $this->mTouched
),
1384 /*WHERE*/ array( 'user_id' => $this->getID() ),
1386 $wgMemc->set( "$wgDBname:user:id:{$this->mId}", $this, 86400 );
1391 * Checks if a user with the given name exists, returns the ID
1393 function idForName() {
1394 $fname = 'User::idForName';
1397 $s = trim( $this->getName() );
1398 if ( 0 == strcmp( '', $s ) ) return 0;
1400 $dbr =& wfGetDB( DB_SLAVE
);
1401 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), $fname );
1402 if ( $id === false ) {
1409 * Add user object to the database
1411 function addToDatabase() {
1412 $fname = 'User::addToDatabase';
1413 $dbw =& wfGetDB( DB_MASTER
);
1414 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
1415 $dbw->insert( 'user',
1417 'user_id' => $seqVal,
1418 'user_name' => $this->mName
,
1419 'user_password' => $this->mPassword
,
1420 'user_newpassword' => $this->mNewpassword
,
1421 'user_email' => $this->mEmail
,
1422 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1423 'user_real_name' => $this->mRealName
,
1424 'user_options' => $this->encodeOptions(),
1425 'user_token' => $this->mToken
1428 $this->mId
= $dbw->insertId();
1431 function spreadBlock() {
1432 # If the (non-anonymous) user is blocked, this function will block any IP address
1433 # that they successfully log on from.
1434 $fname = 'User::spreadBlock';
1436 wfDebug( "User:spreadBlock()\n" );
1437 if ( $this->mId
== 0 ) {
1441 $userblock = Block
::newFromDB( '', $this->mId
);
1442 if ( !$userblock->isValid() ) {
1446 # Check if this IP address is already blocked
1447 $ipblock = Block
::newFromDB( wfGetIP() );
1448 if ( $ipblock->isValid() ) {
1449 # If the user is already blocked. Then check if the autoblock would
1450 # excede the user block. If it would excede, then do nothing, else
1451 # prolong block time
1452 if ($userblock->mExpiry
&&
1453 ($userblock->mExpiry
< Block
::getAutoblockExpiry($ipblock->mTimestamp
))) {
1456 # Just update the timestamp
1457 $ipblock->updateTimestamp();
1461 # Make a new block object with the desired properties
1462 wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
1463 $ipblock->mAddress
= wfGetIP();
1464 $ipblock->mUser
= 0;
1465 $ipblock->mBy
= $userblock->mBy
;
1466 $ipblock->mReason
= wfMsg( 'autoblocker', $this->getName(), $userblock->mReason
);
1467 $ipblock->mTimestamp
= wfTimestampNow();
1468 $ipblock->mAuto
= 1;
1469 # If the user is already blocked with an expiry date, we don't
1470 # want to pile on top of that!
1471 if($userblock->mExpiry
) {
1472 $ipblock->mExpiry
= min ( $userblock->mExpiry
, Block
::getAutoblockExpiry( $ipblock->mTimestamp
));
1474 $ipblock->mExpiry
= Block
::getAutoblockExpiry( $ipblock->mTimestamp
);
1482 function getPageRenderingHash() {
1485 return $this->mHash
;
1488 // stubthreshold is only included below for completeness,
1489 // it will always be 0 when this function is called by parsercache.
1491 $confstr = $this->getOption( 'math' );
1492 $confstr .= '!' . $this->getOption( 'stubthreshold' );
1493 $confstr .= '!' . $this->getOption( 'date' );
1494 $confstr .= '!' . $this->getOption( 'numberheadings' );
1495 $confstr .= '!' . $this->getOption( 'language' );
1496 $confstr .= '!' . $this->getOption( 'thumbsize' );
1497 // add in language specific options, if any
1498 $extra = $wgContLang->getExtraHashOptions();
1501 $this->mHash
= $confstr;
1505 function isAllowedToCreateAccount() {
1506 return $this->isAllowed( 'createaccount' );
1510 * Set mDataLoaded, return previous value
1511 * Use this to prevent DB access in command-line scripts or similar situations
1513 function setLoaded( $loaded ) {
1514 return wfSetVar( $this->mDataLoaded
, $loaded );
1518 * Get this user's personal page title.
1523 function getUserPage() {
1524 return Title
::makeTitle( NS_USER
, $this->getName() );
1528 * Get this user's talk page title.
1533 function getTalkPage() {
1534 $title = $this->getUserPage();
1535 return $title->getTalkPage();
1541 function getMaxID() {
1542 $dbr =& wfGetDB( DB_SLAVE
);
1543 return $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
1547 * Determine whether the user is a newbie. Newbies are either
1548 * anonymous IPs, or the 1% most recently created accounts.
1549 * Bots and sysops are excluded.
1550 * @return bool True if it is a newbie.
1552 function isNewbie() {
1553 return $this->isAnon() ||
$this->mId
> User
::getMaxID() * 0.99 && !$this->isAllowed( 'delete' ) && !$this->isBot();
1557 * Check to see if the given clear-text password is one of the accepted passwords
1558 * @param string $password User password.
1559 * @return bool True if the given password is correct otherwise False.
1561 function checkPassword( $password ) {
1562 global $wgAuth, $wgMinimalPasswordLength;
1563 $this->loadFromDatabase();
1565 // Even though we stop people from creating passwords that
1566 // are shorter than this, doesn't mean people wont be able
1567 // to. Certain authentication plugins do NOT want to save
1568 // domain passwords in a mysql database, so we should
1569 // check this (incase $wgAuth->strict() is false).
1570 if( strlen( $password ) < $wgMinimalPasswordLength ) {
1574 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
1576 } elseif( $wgAuth->strict() ) {
1577 /* Auth plugin doesn't allow local authentication */
1580 $ep = $this->encryptPassword( $password );
1581 if ( 0 == strcmp( $ep, $this->mPassword
) ) {
1583 } elseif ( ($this->mNewpassword
!= '') && (0 == strcmp( $ep, $this->mNewpassword
)) ) {
1585 } elseif ( function_exists( 'iconv' ) ) {
1586 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
1587 # Check for this with iconv
1588 $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
1589 if ( 0 == strcmp( $cp1252hash, $this->mPassword
) ) {
1597 * Initialize (if necessary) and return a session token value
1598 * which can be used in edit forms to show that the user's
1599 * login credentials aren't being hijacked with a foreign form
1602 * @param mixed $salt - Optional function-specific data for hash.
1603 * Use a string or an array of strings.
1607 function editToken( $salt = '' ) {
1608 if( !isset( $_SESSION['wsEditToken'] ) ) {
1609 $token = $this->generateToken();
1610 $_SESSION['wsEditToken'] = $token;
1612 $token = $_SESSION['wsEditToken'];
1614 if( is_array( $salt ) ) {
1615 $salt = implode( '|', $salt );
1617 return md5( $token . $salt );
1621 * Generate a hex-y looking random token for various uses.
1622 * Could be made more cryptographically sure if someone cares.
1625 function generateToken( $salt = '' ) {
1626 $token = dechex( mt_rand() ) . dechex( mt_rand() );
1627 return md5( $token . $salt );
1631 * Check given value against the token value stored in the session.
1632 * A match should confirm that the form was submitted from the
1633 * user's own login session, not a form submission from a third-party
1636 * @param string $val - the input value to compare
1637 * @param string $salt - Optional function-specific data for hash
1641 function matchEditToken( $val, $salt = '' ) {
1645 if ( !isset( $_SESSION['wsEditToken'] ) ) {
1646 $logfile = '/home/wikipedia/logs/session_debug/session.log';
1647 $mckey = memsess_key( session_id() );
1648 $uname = @posix_uname();
1649 $msg = "wsEditToken not set!\n" .
1650 'apache server=' . $uname['nodename'] . "\n" .
1651 'session_id = ' . session_id() . "\n" .
1652 '$_SESSION=' . var_export( $_SESSION, true ) . "\n" .
1653 '$_COOKIE=' . var_export( $_COOKIE, true ) . "\n" .
1654 "mc get($mckey) = " . var_export( $wgMemc->get( $mckey ), true ) . "\n\n\n";
1656 @error_log( $msg, 3, $logfile );
1659 return ( $val == $this->editToken( $salt ) );
1663 * Generate a new e-mail confirmation token and send a confirmation
1664 * mail to the user's given address.
1666 * @return mixed True on success, a WikiError object on failure.
1668 function sendConfirmationMail() {
1670 $url = $this->confirmationTokenUrl( $expiration );
1671 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
1672 wfMsg( 'confirmemail_body',
1676 $wgContLang->timeanddate( $expiration, false ) ) );
1680 * Send an e-mail to this user's account. Does not check for
1681 * confirmed status or validity.
1683 * @param string $subject
1684 * @param string $body
1685 * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
1686 * @return mixed True on success, a WikiError object on failure.
1688 function sendMail( $subject, $body, $from = null ) {
1689 if( is_null( $from ) ) {
1690 global $wgPasswordSender;
1691 $from = $wgPasswordSender;
1694 require_once( 'UserMailer.php' );
1695 $error = userMailer( $this->getEmail(), $from, $subject, $body );
1697 if( $error == '' ) {
1700 return new WikiError( $error );
1705 * Generate, store, and return a new e-mail confirmation code.
1706 * A hash (unsalted since it's used as a key) is stored.
1707 * @param &$expiration mixed output: accepts the expiration time
1711 function confirmationToken( &$expiration ) {
1712 $fname = 'User::confirmationToken';
1715 $expires = $now +
7 * 24 * 60 * 60;
1716 $expiration = wfTimestamp( TS_MW
, $expires );
1718 $token = $this->generateToken( $this->mId
. $this->mEmail
. $expires );
1719 $hash = md5( $token );
1721 $dbw =& wfGetDB( DB_MASTER
);
1722 $dbw->update( 'user',
1723 array( 'user_email_token' => $hash,
1724 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
1725 array( 'user_id' => $this->mId
),
1732 * Generate and store a new e-mail confirmation token, and return
1733 * the URL the user can use to confirm.
1734 * @param &$expiration mixed output: accepts the expiration time
1738 function confirmationTokenUrl( &$expiration ) {
1739 $token = $this->confirmationToken( $expiration );
1740 $title = Title
::makeTitle( NS_SPECIAL
, 'Confirmemail/' . $token );
1741 return $title->getFullUrl();
1745 * Mark the e-mail address confirmed and save.
1747 function confirmEmail() {
1748 $this->loadFromDatabase();
1749 $this->mEmailAuthenticated
= wfTimestampNow();
1750 $this->saveSettings();
1755 * Is this user allowed to send e-mails within limits of current
1756 * site configuration?
1759 function canSendEmail() {
1760 return $this->isEmailConfirmed();
1764 * Is this user allowed to receive e-mails within limits of current
1765 * site configuration?
1768 function canReceiveEmail() {
1769 return $this->canSendEmail() && !$this->getOption( 'disablemail' );
1773 * Is this user's e-mail address valid-looking and confirmed within
1774 * limits of the current site configuration?
1776 * If $wgEmailAuthentication is on, this may require the user to have
1777 * confirmed their address by returning a code or using a password
1778 * sent to the address from the wiki.
1782 function isEmailConfirmed() {
1783 global $wgEmailAuthentication;
1784 $this->loadFromDatabase();
1785 if( $this->isAnon() )
1787 if( !$this->isValidEmailAddr( $this->mEmail
) )
1789 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
1795 * @param array $groups list of groups
1796 * @return array list of permission key names for given groups combined
1799 function getGroupPermissions( $groups ) {
1800 global $wgGroupPermissions;
1802 foreach( $groups as $group ) {
1803 if( isset( $wgGroupPermissions[$group] ) ) {
1804 $rights = array_merge( $rights,
1805 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
1812 * @param string $group key name
1813 * @return string localized descriptive name, if provided
1816 function getGroupName( $group ) {
1817 $key = "group-$group-name";
1818 $name = wfMsg( $key );
1819 if( $name == '' ||
$name == "<$key>" ) {
1827 * Return the set of defined explicit groups.
1828 * The * and 'user' groups are not included.
1832 function getAllGroups() {
1833 global $wgGroupPermissions;
1835 array_keys( $wgGroupPermissions ),
1836 array( '*', 'user' ) );