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', 3 );
28 var $mBlockreason; //!<
29 var $mDataLoaded; //!<
31 var $mEmailAuthenticated; //!<
36 var $mNewpassword; //!<
41 var $mRegistration; //!<
46 var $mVersion; //!< serialized version
49 /** Constructor using User:loadDefaults() */
51 $this->loadDefaults();
52 $this->mVersion
= MW_USER_VERSION
;
56 * Static factory method
57 * @param string $name Username, validated by Title:newFromText()
61 function newFromName( $name ) {
62 # Force usernames to capital
64 $name = $wgContLang->ucfirst( $name );
66 # Clean up name according to title rules
67 $t = Title
::newFromText( $name );
72 # Reject various classes of invalid names
73 $canonicalName = $t->getText();
75 $canonicalName = $wgAuth->getCanonicalName( $t->getText() );
77 if( !User
::isValidUserName( $canonicalName ) ) {
82 $u->setName( $canonicalName );
83 $u->setId( $u->idFromName( $canonicalName ) );
88 * Factory method to fetch whichever use has a given email confirmation code.
89 * This code is generated when an account is created or its e-mail address
92 * If the code is invalid or has expired, returns NULL.
98 function newFromConfirmationCode( $code ) {
99 $dbr =& wfGetDB( DB_SLAVE
);
100 $name = $dbr->selectField( 'user', 'user_name', array(
101 'user_email_token' => md5( $code ),
102 'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
104 if( is_string( $name ) ) {
105 return User
::newFromName( $name );
112 * Serialze sleep function, for better cache efficiency and avoidance of
113 * silly "incomplete type" errors when skins are cached
116 return array( 'mId', 'mName', 'mPassword', 'mEmail', 'mNewtalk',
117 'mEmailAuthenticated', 'mRights', 'mOptions', 'mDataLoaded',
118 'mNewpassword', 'mBlockedby', 'mBlockreason', 'mTouched',
119 'mToken', 'mRealName', 'mHash', 'mGroups', 'mRegistration' );
123 * Get username given an id.
124 * @param integer $id Database user id
125 * @return string Nickname of a user
128 function whoIs( $id ) {
129 $dbr =& wfGetDB( DB_SLAVE
);
130 return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
134 * Get real username given an id.
135 * @param integer $id Database user id
136 * @return string Realname of a user
139 function whoIsReal( $id ) {
140 $dbr =& wfGetDB( DB_SLAVE
);
141 return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), 'User::whoIsReal' );
145 * Get database id given a user name
146 * @param string $name Nickname of a user
147 * @return integer|null Database user id (null: if non existent
150 function idFromName( $name ) {
151 $fname = "User::idFromName";
153 $nt = Title
::newFromText( $name );
154 if( is_null( $nt ) ) {
158 $dbr =& wfGetDB( DB_SLAVE
);
159 $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), $fname );
161 if ( $s === false ) {
169 * does the string match an anonymous IPv4 address?
171 * Note: We match \d{1,3}\.\d{1,3}\.\d{1,3}\.xxx as an anonymous IP
172 * address because the usemod software would "cloak" anonymous IP
173 * addresses like this, if we allowed accounts like this to be created
174 * new users could get the old edits of these anonymous users.
179 * @param string $name Nickname of a user
182 function isIP( $name ) {
183 return preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/",$name);
184 /*return preg_match("/^
185 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
186 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
187 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
188 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))
193 * Is the input a valid username?
195 * Checks if the input is a valid username, we don't want an empty string,
196 * an IP address, anything that containins slashes (would mess up subpages),
197 * is longer than the maximum allowed username size or doesn't begin with
200 * @param string $name
204 function isValidUserName( $name ) {
205 global $wgContLang, $wgMaxNameChars;
208 || User
::isIP( $name )
209 ||
strpos( $name, '/' ) !== false
210 ||
strlen( $name ) > $wgMaxNameChars
211 ||
$name != $wgContLang->ucfirst( $name ) )
214 // Ensure that the name can't be misresolved as a different title,
215 // such as with extra namespace keys at the start.
216 $parsed = Title
::newFromText( $name );
217 if( is_null( $parsed )
218 ||
$parsed->getNamespace()
219 ||
strcmp( $name, $parsed->getPrefixedText() ) )
222 // Check an additional blacklist of troublemaker characters.
223 // Should these be merged into the title char list?
224 $unicodeBlacklist = '/[' .
225 '\x{0080}-\x{009f}' . # iso-8859-1 control chars
226 '\x{00a0}' . # non-breaking space
227 '\x{2000}-\x{200f}' . # various whitespace
228 '\x{2028}-\x{202f}' . # breaks and control chars
229 '\x{3000}' . # ideographic space
230 '\x{e000}-\x{f8ff}' . # private use
232 if( preg_match( $unicodeBlacklist, $name ) ) {
240 * Is the input a valid password?
242 * @param string $password
246 function isValidPassword( $password ) {
247 global $wgMinimalPasswordLength;
248 return strlen( $password ) >= $wgMinimalPasswordLength;
252 * Does the string match roughly an email address ?
254 * There used to be a regular expression here, it got removed because it
255 * rejected valid addresses. Actually just check if there is '@' somewhere
256 * in the given address.
258 * @todo Check for RFC 2822 compilance
261 * @param string $addr email address
265 function isValidEmailAddr ( $addr ) {
266 return ( trim( $addr ) != '' ) &&
267 (false !== strpos( $addr, '@' ) );
271 * Count the number of edits of a user
273 * @param int $uid The user ID to check
276 function edits( $uid ) {
277 $fname = 'User::edits';
279 $dbr =& wfGetDB( DB_SLAVE
);
280 return $dbr->selectField(
281 'revision', 'count(*)',
282 array( 'rev_user' => $uid ),
288 * probably return a random password
289 * @return string probably a random password
291 * @todo Check what is doing really [AV]
293 function randomPassword() {
294 global $wgMinimalPasswordLength;
295 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
296 $l = strlen( $pwchars ) - 1;
298 $pwlength = max( 7, $wgMinimalPasswordLength );
299 $digit = mt_rand(0, $pwlength - 1);
301 for ( $i = 0; $i < $pwlength; $i++
) {
302 $np .= $i == $digit ?
chr( mt_rand(48, 57) ) : $pwchars{ mt_rand(0, $l)};
308 * Set properties to default
309 * Used at construction. It will load per language default settings only
310 * if we have an available language object.
312 function loadDefaults() {
315 $fname = 'User::loadDefaults' . $n;
316 wfProfileIn( $fname );
318 global $wgCookiePrefix;
319 global $wgNamespacesToBeSearchedDefault;
322 $this->mNewtalk
= -1;
323 $this->mName
= false;
324 $this->mRealName
= $this->mEmail
= '';
325 $this->mEmailAuthenticated
= null;
326 $this->mPassword
= $this->mNewpassword
= '';
327 $this->mRights
= array();
328 $this->mGroups
= array();
329 $this->mOptions
= User
::getDefaultOptions();
331 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
332 $this->mOptions
['searchNs'.$nsnum] = $val;
334 unset( $this->mSkin
);
335 $this->mDataLoaded
= false;
336 $this->mBlockedby
= -1; # Unset
337 $this->setToken(); # Random
338 $this->mHash
= false;
340 if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) {
341 $this->mTouched
= wfTimestamp( TS_MW
, $_COOKIE[$wgCookiePrefix.'LoggedOut'] );
344 $this->mTouched
= '0'; # Allow any pages to be cached
347 $this->mRegistration
= wfTimestamp( TS_MW
);
349 wfProfileOut( $fname );
353 * Combine the language default options with any site-specific options
354 * and add the default language variants.
360 function getDefaultOptions() {
362 * Site defaults will override the global/language defaults
364 global $wgContLang, $wgDefaultUserOptions;
365 $defOpt = $wgDefaultUserOptions +
$wgContLang->getDefaultUserOptions();
368 * default language setting
370 $variant = $wgContLang->getPreferredVariant();
371 $defOpt['variant'] = $variant;
372 $defOpt['language'] = $variant;
378 * Get a given default option value.
385 function getDefaultOption( $opt ) {
386 $defOpts = User
::getDefaultOptions();
387 if( isset( $defOpts[$opt] ) ) {
388 return $defOpts[$opt];
395 * Get blocking information
397 * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
398 * non-critical checks are done against slaves. Check when actually saving should be done against
401 function getBlockedStatus( $bFromSlave = true ) {
402 global $wgEnableSorbs, $wgProxyWhitelist;
404 if ( -1 != $this->mBlockedby
) {
405 wfDebug( "User::getBlockedStatus: already loaded.\n" );
409 $fname = 'User::getBlockedStatus';
410 wfProfileIn( $fname );
411 wfDebug( "$fname: checking...\n" );
413 $this->mBlockedby
= 0;
417 $block = new Block();
418 $block->fromMaster( !$bFromSlave );
419 if ( $block->load( $ip , $this->mId
) ) {
420 wfDebug( "$fname: Found block.\n" );
421 $this->mBlockedby
= $block->mBy
;
422 $this->mBlockreason
= $block->mReason
;
423 if ( $this->isLoggedIn() ) {
424 $this->spreadBlock();
427 wfDebug( "$fname: No block.\n" );
431 if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) {
434 if ( wfIsLocallyBlockedProxy( $ip ) ) {
435 $this->mBlockedby
= wfMsg( 'proxyblocker' );
436 $this->mBlockreason
= wfMsg( 'proxyblockreason' );
440 if ( !$this->mBlockedby
&& $wgEnableSorbs && !$this->getID() ) {
441 if ( $this->inSorbsBlacklist( $ip ) ) {
442 $this->mBlockedby
= wfMsg( 'sorbs' );
443 $this->mBlockreason
= wfMsg( 'sorbsreason' );
449 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
451 wfProfileOut( $fname );
454 function inSorbsBlacklist( $ip ) {
455 global $wgEnableSorbs;
456 return $wgEnableSorbs &&
457 $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
460 function inOpmBlacklist( $ip ) {
462 return $wgEnableOpm &&
463 $this->inDnsBlacklist( $ip, 'opm.blitzed.org.' );
466 function inDnsBlacklist( $ip, $base ) {
467 $fname = 'User::inDnsBlacklist';
468 wfProfileIn( $fname );
473 if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
475 for ( $i=4; $i>=1; $i-- ) {
476 $host .= $m[$i] . '.';
481 $ipList = gethostbynamel( $host );
484 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
487 wfDebug( "Requested $host, not found in $base.\n" );
491 wfProfileOut( $fname );
496 * Primitive rate limits: enforce maximum actions per time period
497 * to put a brake on flooding.
499 * Note: when using a shared cache like memcached, IP-address
500 * last-hit counters will be shared across wikis.
502 * @return bool true if a rate limiter was tripped
505 function pingLimiter( $action='edit' ) {
506 global $wgRateLimits;
507 if( !isset( $wgRateLimits[$action] ) ) {
510 if( $this->isAllowed( 'delete' ) ) {
515 global $wgMemc, $wgDBname, $wgRateLimitLog;
516 $fname = 'User::pingLimiter';
517 wfProfileIn( $fname );
519 $limits = $wgRateLimits[$action];
521 $id = $this->getId();
524 if( isset( $limits['anon'] ) && $id == 0 ) {
525 $keys["$wgDBname:limiter:$action:anon"] = $limits['anon'];
528 if( isset( $limits['user'] ) && $id != 0 ) {
529 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['user'];
531 if( $this->isNewbie() ) {
532 if( isset( $limits['newbie'] ) && $id != 0 ) {
533 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['newbie'];
535 if( isset( $limits['ip'] ) ) {
536 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
538 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
539 $subnet = $matches[1];
540 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
545 foreach( $keys as $key => $limit ) {
546 list( $max, $period ) = $limit;
547 $summary = "(limit $max in {$period}s)";
548 $count = $wgMemc->get( $key );
550 if( $count > $max ) {
551 wfDebug( "$fname: tripped! $key at $count $summary\n" );
552 if( $wgRateLimitLog ) {
553 @error_log
( wfTimestamp( TS_MW
) . ' ' . $wgDBname . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
557 wfDebug( "$fname: ok. $key at $count $summary\n" );
560 wfDebug( "$fname: adding record for $key $summary\n" );
561 $wgMemc->add( $key, 1, intval( $period ) );
563 $wgMemc->incr( $key );
566 wfProfileOut( $fname );
571 * Check if user is blocked
572 * @return bool True if blocked, false otherwise
574 function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
575 wfDebug( "User::isBlocked: enter\n" );
576 $this->getBlockedStatus( $bFromSlave );
577 return $this->mBlockedby
!== 0;
581 * Check if user is blocked from editing a particular article
583 function isBlockedFrom( $title, $bFromSlave = false ) {
584 global $wgBlockAllowsUTEdit;
585 $fname = 'User::isBlockedFrom';
586 wfProfileIn( $fname );
587 wfDebug( "$fname: enter\n" );
589 if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
590 $title->getNamespace() == NS_USER_TALK
)
593 wfDebug( "$fname: self-talk page, ignoring any blocks\n" );
595 wfDebug( "$fname: asking isBlocked()\n" );
596 $blocked = $this->isBlocked( $bFromSlave );
598 wfProfileOut( $fname );
603 * Get name of blocker
604 * @return string name of blocker
606 function blockedBy() {
607 $this->getBlockedStatus();
608 return $this->mBlockedby
;
612 * Get blocking reason
613 * @return string Blocking reason
615 function blockedFor() {
616 $this->getBlockedStatus();
617 return $this->mBlockreason
;
621 * Initialise php session
623 function SetupSession() {
624 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
625 if( $wgSessionsInMemcached ) {
626 require_once( 'MemcachedSessions.php' );
627 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
628 # If it's left on 'user' or another setting from another
629 # application, it will end up failing. Try to recover.
630 ini_set ( 'session.save_handler', 'files' );
632 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
633 session_cache_limiter( 'private, must-revalidate' );
638 * Create a new user object using data from session
641 function loadFromSession() {
642 global $wgMemc, $wgDBname, $wgCookiePrefix;
644 if ( isset( $_SESSION['wsUserID'] ) ) {
645 if ( 0 != $_SESSION['wsUserID'] ) {
646 $sId = $_SESSION['wsUserID'];
650 } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserID"] ) ) {
651 $sId = intval( $_COOKIE["{$wgCookiePrefix}UserID"] );
652 $_SESSION['wsUserID'] = $sId;
656 if ( isset( $_SESSION['wsUserName'] ) ) {
657 $sName = $_SESSION['wsUserName'];
658 } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserName"] ) ) {
659 $sName = $_COOKIE["{$wgCookiePrefix}UserName"];
660 $_SESSION['wsUserName'] = $sName;
665 $passwordCorrect = FALSE;
666 $user = $wgMemc->get( $key = "$wgDBname:user:id:$sId" );
667 if( !is_object( $user ) ||
$user->mVersion
< MW_USER_VERSION
) {
668 # Expire old serialized objects; they may be corrupt.
671 if($makenew = !$user) {
672 wfDebug( "User::loadFromSession() unable to load from memcached\n" );
675 $user->loadFromDatabase();
677 wfDebug( "User::loadFromSession() got from cache!\n" );
680 if ( isset( $_SESSION['wsToken'] ) ) {
681 $passwordCorrect = $_SESSION['wsToken'] == $user->mToken
;
682 } else if ( isset( $_COOKIE["{$wgCookiePrefix}Token"] ) ) {
683 $passwordCorrect = $user->mToken
== $_COOKIE["{$wgCookiePrefix}Token"];
685 return new User(); # Can't log in from session
688 if ( ( $sName == $user->mName
) && $passwordCorrect ) {
690 if($wgMemc->set( $key, $user ))
691 wfDebug( "User::loadFromSession() successfully saved user\n" );
693 wfDebug( "User::loadFromSession() unable to save to memcached\n" );
697 return new User(); # Can't log in from session
701 * Load a user from the database
703 function loadFromDatabase() {
704 $fname = "User::loadFromDatabase";
706 # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress
707 # loading in a command line script, don't assume all command line scripts need it like this
708 #if ( $this->mDataLoaded || $wgCommandLineMode ) {
709 if ( $this->mDataLoaded
) {
714 $this->mId
= intval( $this->mId
);
716 /** Anonymous user */
719 $this->mRights
= $this->getGroupPermissions( array( '*' ) );
720 $this->mDataLoaded
= true;
722 } # the following stuff is for non-anonymous users only
724 $dbr =& wfGetDB( DB_SLAVE
);
725 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
726 'user_email_authenticated',
727 'user_real_name','user_options','user_touched', 'user_token', 'user_registration' ),
728 array( 'user_id' => $this->mId
), $fname );
730 if ( $s !== false ) {
731 $this->mName
= $s->user_name
;
732 $this->mEmail
= $s->user_email
;
733 $this->mEmailAuthenticated
= wfTimestampOrNull( TS_MW
, $s->user_email_authenticated
);
734 $this->mRealName
= $s->user_real_name
;
735 $this->mPassword
= $s->user_password
;
736 $this->mNewpassword
= $s->user_newpassword
;
737 $this->decodeOptions( $s->user_options
);
738 $this->mTouched
= wfTimestamp(TS_MW
,$s->user_touched
);
739 $this->mToken
= $s->user_token
;
740 $this->mRegistration
= wfTimestampOrNull( TS_MW
, $s->user_registration
);
742 $res = $dbr->select( 'user_groups',
744 array( 'ug_user' => $this->mId
),
746 $this->mGroups
= array();
747 while( $row = $dbr->fetchObject( $res ) ) {
748 $this->mGroups
[] = $row->ug_group
;
750 $implicitGroups = array( '*', 'user' );
752 global $wgAutoConfirmAge;
753 $accountAge = time() - wfTimestampOrNull( TS_UNIX
, $this->mRegistration
);
754 if( $accountAge >= $wgAutoConfirmAge ) {
755 $implicitGroups[] = 'autoconfirmed';
758 $effectiveGroups = array_merge( $implicitGroups, $this->mGroups
);
759 $this->mRights
= $this->getGroupPermissions( $effectiveGroups );
762 $this->mDataLoaded
= true;
765 function getID() { return $this->mId
; }
766 function setID( $v ) {
768 $this->mDataLoaded
= false;
772 $this->loadFromDatabase();
773 if ( $this->mName
=== false ) {
774 $this->mName
= wfGetIP();
779 function setName( $str ) {
780 $this->loadFromDatabase();
786 * Return the title dbkey form of the name, for eg user pages.
790 function getTitleKey() {
791 return str_replace( ' ', '_', $this->getName() );
794 function getNewtalk() {
795 $this->loadFromDatabase();
797 # Load the newtalk status if it is unloaded (mNewtalk=-1)
798 if( $this->mNewtalk
=== -1 ) {
799 $this->mNewtalk
= false; # reset talk page status
801 # Check memcached separately for anons, who have no
802 # entire User object stored in there.
804 global $wgDBname, $wgMemc;
805 $key = "$wgDBname:newtalk:ip:" . $this->getName();
806 $newtalk = $wgMemc->get( $key );
807 if( is_integer( $newtalk ) ) {
808 $this->mNewtalk
= (bool)$newtalk;
810 $this->mNewtalk
= $this->checkNewtalk( 'user_ip', $this->getName() );
811 $wgMemc->set( $key, $this->mNewtalk
, time() ); // + 1800 );
814 $this->mNewtalk
= $this->checkNewtalk( 'user_id', $this->mId
);
818 return (bool)$this->mNewtalk
;
822 * Return the talk page(s) this user has new messages on.
824 function getNewMessageLinks() {
827 if (!wfRunHooks('UserRetrieveNewTalks', array(&$this, &$talks)))
830 if (!$this->getNewtalk())
832 $up = $this->getUserPage();
833 $utp = $up->getTalkPage();
834 return array(array("wiki" => $wgDBname, "link" => $utp->getLocalURL()));
839 * Perform a user_newtalk check on current slaves; if the memcached data
840 * is funky we don't want newtalk state to get stuck on save, as that's
843 * @param string $field
848 function checkNewtalk( $field, $id ) {
849 $fname = 'User::checkNewtalk';
850 $dbr =& wfGetDB( DB_SLAVE
);
851 $ok = $dbr->selectField( 'user_newtalk', $field,
852 array( $field => $id ), $fname );
853 return $ok !== false;
858 * @param string $field
862 function updateNewtalk( $field, $id ) {
863 $fname = 'User::updateNewtalk';
864 if( $this->checkNewtalk( $field, $id ) ) {
865 wfDebug( "$fname already set ($field, $id), ignoring\n" );
868 $dbw =& wfGetDB( DB_MASTER
);
869 $dbw->insert( 'user_newtalk',
870 array( $field => $id ),
873 wfDebug( "$fname: set on ($field, $id)\n" );
878 * Clear the new messages flag for the given user
879 * @param string $field
883 function deleteNewtalk( $field, $id ) {
884 $fname = 'User::deleteNewtalk';
885 if( !$this->checkNewtalk( $field, $id ) ) {
886 wfDebug( "$fname: already gone ($field, $id), ignoring\n" );
889 $dbw =& wfGetDB( DB_MASTER
);
890 $dbw->delete( 'user_newtalk',
891 array( $field => $id ),
893 wfDebug( "$fname: killed on ($field, $id)\n" );
898 * Update the 'You have new messages!' status.
901 function setNewtalk( $val ) {
906 $this->loadFromDatabase();
907 $this->mNewtalk
= $val;
909 $fname = 'User::setNewtalk';
911 if( $this->isAnon() ) {
913 $id = $this->getName();
916 $id = $this->getId();
920 $changed = $this->updateNewtalk( $field, $id );
922 $changed = $this->deleteNewtalk( $field, $id );
926 if( $this->isAnon() ) {
927 // Anons have a separate memcached space, since
928 // user records aren't kept for them.
929 global $wgDBname, $wgMemc;
930 $key = "$wgDBname:newtalk:ip:$val";
931 $wgMemc->set( $key, $val ?
1 : 0 );
934 // Make sure the user page is watched, so a notification
935 // will be sent out if enabled.
936 $this->addWatch( $this->getTalkPage() );
939 $this->invalidateCache();
940 $this->saveSettings();
944 function invalidateCache() {
945 global $wgClockSkewFudge;
946 $this->loadFromDatabase();
947 $this->mTouched
= wfTimestamp(TS_MW
, time() +
$wgClockSkewFudge );
948 # Don't forget to save the options after this or
949 # it won't take effect!
952 function validateCache( $timestamp ) {
953 $this->loadFromDatabase();
954 return ($timestamp >= $this->mTouched
);
958 * Encrypt a password.
959 * It can eventuall salt a password @see User::addSalt()
960 * @param string $p clear Password.
961 * @return string Encrypted password.
963 function encryptPassword( $p ) {
964 return wfEncryptPassword( $this->mId
, $p );
967 # Set the password and reset the random token
968 function setPassword( $str ) {
969 $this->loadFromDatabase();
971 $this->mPassword
= $this->encryptPassword( $str );
972 $this->mNewpassword
= '';
975 # Set the random token (used for persistent authentication)
976 function setToken( $token = false ) {
977 global $wgSecretKey, $wgProxyKey, $wgDBname;
979 if ( $wgSecretKey ) {
981 } elseif ( $wgProxyKey ) {
986 $this->mToken
= md5( $key . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId
);
988 $this->mToken
= $token;
993 function setCookiePassword( $str ) {
994 $this->loadFromDatabase();
995 $this->mCookiePassword
= md5( $str );
998 function setNewpassword( $str ) {
999 $this->loadFromDatabase();
1000 $this->mNewpassword
= $this->encryptPassword( $str );
1003 function getEmail() {
1004 $this->loadFromDatabase();
1005 return $this->mEmail
;
1008 function getEmailAuthenticationTimestamp() {
1009 $this->loadFromDatabase();
1010 return $this->mEmailAuthenticated
;
1013 function setEmail( $str ) {
1014 $this->loadFromDatabase();
1015 $this->mEmail
= $str;
1018 function getRealName() {
1019 $this->loadFromDatabase();
1020 return $this->mRealName
;
1023 function setRealName( $str ) {
1024 $this->loadFromDatabase();
1025 $this->mRealName
= $str;
1029 * @param string $oname The option to check
1032 function getOption( $oname ) {
1033 $this->loadFromDatabase();
1034 if ( array_key_exists( $oname, $this->mOptions
) ) {
1035 return trim( $this->mOptions
[$oname] );
1042 * @param string $oname The option to check
1043 * @return bool False if the option is not selected, true if it is
1045 function getBoolOption( $oname ) {
1046 return (bool)$this->getOption( $oname );
1049 function setOption( $oname, $val ) {
1050 $this->loadFromDatabase();
1051 if ( $oname == 'skin' ) {
1052 # Clear cached skin, so the new one displays immediately in Special:Preferences
1053 unset( $this->mSkin
);
1055 $this->mOptions
[$oname] = $val;
1056 $this->invalidateCache();
1059 function getRights() {
1060 $this->loadFromDatabase();
1061 return $this->mRights
;
1065 * Get the list of explicit group memberships this user has.
1066 * The implicit * and user groups are not included.
1067 * @return array of strings
1069 function getGroups() {
1070 $this->loadFromDatabase();
1071 return $this->mGroups
;
1075 * Get the list of implicit group memberships this user has.
1076 * This includes all explicit groups, plus 'user' if logged in
1077 * and '*' for all accounts.
1078 * @return array of strings
1080 function getEffectiveGroups() {
1081 $base = array( '*' );
1082 if( $this->isLoggedIn() ) {
1085 return array_merge( $base, $this->getGroups() );
1089 * Add the user to the given group.
1090 * This takes immediate effect.
1093 function addGroup( $group ) {
1094 $dbw =& wfGetDB( DB_MASTER
);
1095 $dbw->insert( 'user_groups',
1097 'ug_user' => $this->getID(),
1098 'ug_group' => $group,
1101 array( 'IGNORE' ) );
1103 $this->mGroups
= array_merge( $this->mGroups
, array( $group ) );
1104 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1106 $this->invalidateCache();
1107 $this->saveSettings();
1111 * Remove the user from the given group.
1112 * This takes immediate effect.
1115 function removeGroup( $group ) {
1116 $dbw =& wfGetDB( DB_MASTER
);
1117 $dbw->delete( 'user_groups',
1119 'ug_user' => $this->getID(),
1120 'ug_group' => $group,
1122 'User::removeGroup' );
1124 $this->mGroups
= array_diff( $this->mGroups
, array( $group ) );
1125 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1127 $this->invalidateCache();
1128 $this->saveSettings();
1133 * A more legible check for non-anonymousness.
1134 * Returns true if the user is not an anonymous visitor.
1138 function isLoggedIn() {
1139 return( $this->getID() != 0 );
1143 * A more legible check for anonymousness.
1144 * Returns true if the user is an anonymous visitor.
1149 return !$this->isLoggedIn();
1153 * Check if a user is sysop
1156 function isSysop() {
1157 return $this->isAllowed( 'protect' );
1161 function isDeveloper() {
1162 return $this->isAllowed( 'siteadmin' );
1166 function isBureaucrat() {
1167 return $this->isAllowed( 'makesysop' );
1171 * Whether the user is a bot
1172 * @todo need to be migrated to the new user level management sytem
1175 $this->loadFromDatabase();
1176 return in_array( 'bot', $this->mRights
);
1180 * Check if user is allowed to access a feature / make an action
1181 * @param string $action Action to be checked (see $wgAvailableRights in Defines.php for possible actions).
1182 * @return boolean True: action is allowed, False: action should not be allowed
1184 function isAllowed($action='') {
1185 if ( $action === '' )
1186 // In the spirit of DWIM
1189 $this->loadFromDatabase();
1190 return in_array( $action , $this->mRights
);
1194 * Load a skin if it doesn't exist or return it
1195 * @todo FIXME : need to check the old failback system [AV]
1197 function &getSkin() {
1198 global $IP, $wgRequest;
1199 if ( ! isset( $this->mSkin
) ) {
1200 $fname = 'User::getSkin';
1201 wfProfileIn( $fname );
1204 $userSkin = $this->getOption( 'skin' );
1205 $userSkin = $wgRequest->getVal('useskin', $userSkin);
1207 $this->mSkin
=& Skin
::newFromKey( $userSkin );
1208 wfProfileOut( $fname );
1210 return $this->mSkin
;
1214 * @param string $title Article title to look at
1218 * Check watched status of an article
1219 * @return bool True if article is watched
1221 function isWatched( $title ) {
1222 $wl = WatchedItem
::fromUserTitle( $this, $title );
1223 return $wl->isWatched();
1229 function addWatch( $title ) {
1230 $wl = WatchedItem
::fromUserTitle( $this, $title );
1232 $this->invalidateCache();
1236 * Stop watching an article
1238 function removeWatch( $title ) {
1239 $wl = WatchedItem
::fromUserTitle( $this, $title );
1241 $this->invalidateCache();
1245 * Clear the user's notification timestamp for the given title.
1246 * If e-notif e-mails are on, they will receive notification mails on
1247 * the next change of the page if it's watched etc.
1249 function clearNotification( &$title ) {
1250 global $wgUser, $wgUseEnotif;
1253 if ($title->getNamespace() == NS_USER_TALK
&&
1254 $title->getText() == $this->getName() ) {
1255 if (!wfRunHooks('UserClearNewTalkNotification', array(&$this)))
1257 $this->setNewtalk( false );
1260 if( !$wgUseEnotif ) {
1264 if( $this->isAnon() ) {
1265 // Nothing else to do...
1269 // Only update the timestamp if the page is being watched.
1270 // The query to find out if it is watched is cached both in memcached and per-invocation,
1271 // and when it does have to be executed, it can be on a slave
1272 // If this is the user's newtalk page, we always update the timestamp
1273 if ($title->getNamespace() == NS_USER_TALK
&&
1274 $title->getText() == $wgUser->getName())
1277 } elseif ( $this->getID() == $wgUser->getID() ) {
1278 $watched = $title->userIsWatching();
1283 // If the page is watched by the user (or may be watched), update the timestamp on any
1284 // any matching rows
1286 $dbw =& wfGetDB( DB_MASTER
);
1287 $success = $dbw->update( 'watchlist',
1289 'wl_notificationtimestamp' => NULL
1290 ), array( /* WHERE */
1291 'wl_title' => $title->getDBkey(),
1292 'wl_namespace' => $title->getNamespace(),
1293 'wl_user' => $this->getID()
1294 ), 'User::clearLastVisited'
1302 * Resets all of the given user's page-change notification timestamps.
1303 * If e-notif e-mails are on, they will receive notification mails on
1304 * the next change of any watched page.
1306 * @param int $currentUser user ID number
1309 function clearAllNotifications( $currentUser ) {
1310 global $wgUseEnotif;
1311 if ( !$wgUseEnotif ) {
1312 $this->setNewtalk( false );
1315 if( $currentUser != 0 ) {
1317 $dbw =& wfGetDB( DB_MASTER
);
1318 $success = $dbw->update( 'watchlist',
1320 'wl_notificationtimestamp' => 0
1321 ), array( /* WHERE */
1322 'wl_user' => $currentUser
1323 ), 'UserMailer::clearAll'
1326 # we also need to clear here the "you have new message" notification for the own user_talk page
1327 # This is cleared one page view later in Article::viewUpdates();
1333 * @return string Encoding options
1335 function encodeOptions() {
1337 foreach ( $this->mOptions
as $oname => $oval ) {
1338 array_push( $a, $oname.'='.$oval );
1340 $s = implode( "\n", $a );
1347 function decodeOptions( $str ) {
1348 $a = explode( "\n", $str );
1349 foreach ( $a as $s ) {
1350 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
1351 $this->mOptions
[$m[1]] = $m[2];
1356 function setCookies() {
1357 global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix;
1358 if ( 0 == $this->mId
) return;
1359 $this->loadFromDatabase();
1360 $exp = time() +
$wgCookieExpiration;
1362 $_SESSION['wsUserID'] = $this->mId
;
1363 setcookie( $wgCookiePrefix.'UserID', $this->mId
, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1365 $_SESSION['wsUserName'] = $this->getName();
1366 setcookie( $wgCookiePrefix.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1368 $_SESSION['wsToken'] = $this->mToken
;
1369 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
1370 setcookie( $wgCookiePrefix.'Token', $this->mToken
, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1372 setcookie( $wgCookiePrefix.'Token', '', time() - 3600 );
1378 * It will clean the session cookie
1381 global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix;
1382 $this->loadDefaults();
1383 $this->setLoaded( true );
1385 $_SESSION['wsUserID'] = 0;
1387 setcookie( $wgCookiePrefix.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1388 setcookie( $wgCookiePrefix.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1390 # Remember when user logged out, to prevent seeing cached pages
1391 setcookie( $wgCookiePrefix.'LoggedOut', wfTimestampNow(), time() +
86400, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1395 * Save object settings into database
1397 function saveSettings() {
1398 global $wgMemc, $wgDBname;
1399 $fname = 'User::saveSettings';
1401 if ( wfReadOnly() ) { return; }
1402 if ( 0 == $this->mId
) { return; }
1404 $dbw =& wfGetDB( DB_MASTER
);
1405 $dbw->update( 'user',
1407 'user_name' => $this->mName
,
1408 'user_password' => $this->mPassword
,
1409 'user_newpassword' => $this->mNewpassword
,
1410 'user_real_name' => $this->mRealName
,
1411 'user_email' => $this->mEmail
,
1412 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1413 'user_options' => $this->encodeOptions(),
1414 'user_touched' => $dbw->timestamp($this->mTouched
),
1415 'user_token' => $this->mToken
1416 ), array( /* WHERE */
1417 'user_id' => $this->mId
1420 $wgMemc->delete( "$wgDBname:user:id:$this->mId" );
1425 * Checks if a user with the given name exists, returns the ID
1427 function idForName() {
1428 $fname = 'User::idForName';
1431 $s = trim( $this->getName() );
1432 if ( 0 == strcmp( '', $s ) ) return 0;
1434 $dbr =& wfGetDB( DB_SLAVE
);
1435 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), $fname );
1436 if ( $id === false ) {
1443 * Add user object to the database
1445 function addToDatabase() {
1446 $fname = 'User::addToDatabase';
1447 $dbw =& wfGetDB( DB_MASTER
);
1448 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
1449 $dbw->insert( 'user',
1451 'user_id' => $seqVal,
1452 'user_name' => $this->mName
,
1453 'user_password' => $this->mPassword
,
1454 'user_newpassword' => $this->mNewpassword
,
1455 'user_email' => $this->mEmail
,
1456 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1457 'user_real_name' => $this->mRealName
,
1458 'user_options' => $this->encodeOptions(),
1459 'user_token' => $this->mToken
,
1460 'user_registration' => $dbw->timestamp( $this->mRegistration
),
1463 $this->mId
= $dbw->insertId();
1466 function spreadBlock() {
1467 # If the (non-anonymous) user is blocked, this function will block any IP address
1468 # that they successfully log on from.
1469 $fname = 'User::spreadBlock';
1471 wfDebug( "User:spreadBlock()\n" );
1472 if ( $this->mId
== 0 ) {
1476 $userblock = Block
::newFromDB( '', $this->mId
);
1477 if ( !$userblock->isValid() ) {
1481 # Check if this IP address is already blocked
1482 $ipblock = Block
::newFromDB( wfGetIP() );
1483 if ( $ipblock->isValid() ) {
1484 # If the user is already blocked. Then check if the autoblock would
1485 # excede the user block. If it would excede, then do nothing, else
1486 # prolong block time
1487 if ($userblock->mExpiry
&&
1488 ($userblock->mExpiry
< Block
::getAutoblockExpiry($ipblock->mTimestamp
))) {
1491 # Just update the timestamp
1492 $ipblock->updateTimestamp();
1496 # Make a new block object with the desired properties
1497 wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
1498 $ipblock->mAddress
= wfGetIP();
1499 $ipblock->mUser
= 0;
1500 $ipblock->mBy
= $userblock->mBy
;
1501 $ipblock->mReason
= wfMsg( 'autoblocker', $this->getName(), $userblock->mReason
);
1502 $ipblock->mTimestamp
= wfTimestampNow();
1503 $ipblock->mAuto
= 1;
1504 # If the user is already blocked with an expiry date, we don't
1505 # want to pile on top of that!
1506 if($userblock->mExpiry
) {
1507 $ipblock->mExpiry
= min ( $userblock->mExpiry
, Block
::getAutoblockExpiry( $ipblock->mTimestamp
));
1509 $ipblock->mExpiry
= Block
::getAutoblockExpiry( $ipblock->mTimestamp
);
1518 * Generate a string which will be different for any combination of
1519 * user options which would produce different parser output.
1520 * This will be used as part of the hash key for the parser cache,
1521 * so users will the same options can share the same cached data
1524 * Extensions which require it should install 'PageRenderingHash' hook,
1525 * which will give them a chance to modify this key based on their own
1530 function getPageRenderingHash() {
1533 return $this->mHash
;
1536 // stubthreshold is only included below for completeness,
1537 // it will always be 0 when this function is called by parsercache.
1539 $confstr = $this->getOption( 'math' );
1540 $confstr .= '!' . $this->getOption( 'stubthreshold' );
1541 $confstr .= '!' . $this->getOption( 'date' );
1542 $confstr .= '!' . ($this->getOption( 'numberheadings' ) ?
'1' : '');
1543 $confstr .= '!' . $this->getOption( 'language' );
1544 $confstr .= '!' . $this->getOption( 'thumbsize' );
1545 // add in language specific options, if any
1546 $extra = $wgContLang->getExtraHashOptions();
1549 // Give a chance for extensions to modify the hash, if they have
1550 // extra options or other effects on the parser cache.
1551 wfRunHooks( 'PageRenderingHash', array( &$confstr ) );
1553 $this->mHash
= $confstr;
1557 function isAllowedToCreateAccount() {
1558 return $this->isAllowed( 'createaccount' ) && !$this->isBlocked();
1562 * Set mDataLoaded, return previous value
1563 * Use this to prevent DB access in command-line scripts or similar situations
1565 function setLoaded( $loaded ) {
1566 return wfSetVar( $this->mDataLoaded
, $loaded );
1570 * Get this user's personal page title.
1575 function getUserPage() {
1576 return Title
::makeTitle( NS_USER
, $this->getName() );
1580 * Get this user's talk page title.
1585 function getTalkPage() {
1586 $title = $this->getUserPage();
1587 return $title->getTalkPage();
1593 function getMaxID() {
1594 static $res; // cache
1596 if ( isset( $res ) )
1599 $dbr =& wfGetDB( DB_SLAVE
);
1600 return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
1605 * Determine whether the user is a newbie. Newbies are either
1606 * anonymous IPs, or the most recently created accounts.
1607 * @return bool True if it is a newbie.
1609 function isNewbie() {
1610 return !$this->isAllowed( 'autoconfirmed' );
1614 * Check to see if the given clear-text password is one of the accepted passwords
1615 * @param string $password User password.
1616 * @return bool True if the given password is correct otherwise False.
1618 function checkPassword( $password ) {
1619 global $wgAuth, $wgMinimalPasswordLength;
1620 $this->loadFromDatabase();
1622 // Even though we stop people from creating passwords that
1623 // are shorter than this, doesn't mean people wont be able
1624 // to. Certain authentication plugins do NOT want to save
1625 // domain passwords in a mysql database, so we should
1626 // check this (incase $wgAuth->strict() is false).
1627 if( strlen( $password ) < $wgMinimalPasswordLength ) {
1631 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
1633 } elseif( $wgAuth->strict() ) {
1634 /* Auth plugin doesn't allow local authentication */
1637 $ep = $this->encryptPassword( $password );
1638 if ( 0 == strcmp( $ep, $this->mPassword
) ) {
1640 } elseif ( ($this->mNewpassword
!= '') && (0 == strcmp( $ep, $this->mNewpassword
)) ) {
1642 } elseif ( function_exists( 'iconv' ) ) {
1643 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
1644 # Check for this with iconv
1645 $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
1646 if ( 0 == strcmp( $cp1252hash, $this->mPassword
) ) {
1654 * Initialize (if necessary) and return a session token value
1655 * which can be used in edit forms to show that the user's
1656 * login credentials aren't being hijacked with a foreign form
1659 * @param mixed $salt - Optional function-specific data for hash.
1660 * Use a string or an array of strings.
1664 function editToken( $salt = '' ) {
1665 if( !isset( $_SESSION['wsEditToken'] ) ) {
1666 $token = $this->generateToken();
1667 $_SESSION['wsEditToken'] = $token;
1669 $token = $_SESSION['wsEditToken'];
1671 if( is_array( $salt ) ) {
1672 $salt = implode( '|', $salt );
1674 return md5( $token . $salt );
1678 * Generate a hex-y looking random token for various uses.
1679 * Could be made more cryptographically sure if someone cares.
1682 function generateToken( $salt = '' ) {
1683 $token = dechex( mt_rand() ) . dechex( mt_rand() );
1684 return md5( $token . $salt );
1688 * Check given value against the token value stored in the session.
1689 * A match should confirm that the form was submitted from the
1690 * user's own login session, not a form submission from a third-party
1693 * @param string $val - the input value to compare
1694 * @param string $salt - Optional function-specific data for hash
1698 function matchEditToken( $val, $salt = '' ) {
1700 $sessionToken = $this->editToken( $salt );
1701 if ( $val != $sessionToken ) {
1702 wfDebug( "User::matchEditToken: broken session data\n" );
1704 return $val == $sessionToken;
1708 * Generate a new e-mail confirmation token and send a confirmation
1709 * mail to the user's given address.
1711 * @return mixed True on success, a WikiError object on failure.
1713 function sendConfirmationMail() {
1715 $url = $this->confirmationTokenUrl( $expiration );
1716 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
1717 wfMsg( 'confirmemail_body',
1721 $wgContLang->timeanddate( $expiration, false ) ) );
1725 * Send an e-mail to this user's account. Does not check for
1726 * confirmed status or validity.
1728 * @param string $subject
1729 * @param string $body
1730 * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
1731 * @return mixed True on success, a WikiError object on failure.
1733 function sendMail( $subject, $body, $from = null ) {
1734 if( is_null( $from ) ) {
1735 global $wgPasswordSender;
1736 $from = $wgPasswordSender;
1739 require_once( 'UserMailer.php' );
1740 $to = new MailAddress( $this );
1741 $sender = new MailAddress( $from );
1742 $error = userMailer( $to, $sender, $subject, $body );
1744 if( $error == '' ) {
1747 return new WikiError( $error );
1752 * Generate, store, and return a new e-mail confirmation code.
1753 * A hash (unsalted since it's used as a key) is stored.
1754 * @param &$expiration mixed output: accepts the expiration time
1758 function confirmationToken( &$expiration ) {
1759 $fname = 'User::confirmationToken';
1762 $expires = $now +
7 * 24 * 60 * 60;
1763 $expiration = wfTimestamp( TS_MW
, $expires );
1765 $token = $this->generateToken( $this->mId
. $this->mEmail
. $expires );
1766 $hash = md5( $token );
1768 $dbw =& wfGetDB( DB_MASTER
);
1769 $dbw->update( 'user',
1770 array( 'user_email_token' => $hash,
1771 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
1772 array( 'user_id' => $this->mId
),
1779 * Generate and store a new e-mail confirmation token, and return
1780 * the URL the user can use to confirm.
1781 * @param &$expiration mixed output: accepts the expiration time
1785 function confirmationTokenUrl( &$expiration ) {
1786 $token = $this->confirmationToken( $expiration );
1787 $title = Title
::makeTitle( NS_SPECIAL
, 'Confirmemail/' . $token );
1788 return $title->getFullUrl();
1792 * Mark the e-mail address confirmed and save.
1794 function confirmEmail() {
1795 $this->loadFromDatabase();
1796 $this->mEmailAuthenticated
= wfTimestampNow();
1797 $this->saveSettings();
1802 * Is this user allowed to send e-mails within limits of current
1803 * site configuration?
1806 function canSendEmail() {
1807 return $this->isEmailConfirmed();
1811 * Is this user allowed to receive e-mails within limits of current
1812 * site configuration?
1815 function canReceiveEmail() {
1816 return $this->canSendEmail() && !$this->getOption( 'disablemail' );
1820 * Is this user's e-mail address valid-looking and confirmed within
1821 * limits of the current site configuration?
1823 * If $wgEmailAuthentication is on, this may require the user to have
1824 * confirmed their address by returning a code or using a password
1825 * sent to the address from the wiki.
1829 function isEmailConfirmed() {
1830 global $wgEmailAuthentication;
1831 $this->loadFromDatabase();
1832 if( $this->isAnon() )
1834 if( !$this->isValidEmailAddr( $this->mEmail
) )
1836 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
1842 * @param array $groups list of groups
1843 * @return array list of permission key names for given groups combined
1846 function getGroupPermissions( $groups ) {
1847 global $wgGroupPermissions;
1849 foreach( $groups as $group ) {
1850 if( isset( $wgGroupPermissions[$group] ) ) {
1851 $rights = array_merge( $rights,
1852 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
1859 * @param string $group key name
1860 * @return string localized descriptive name, if provided
1863 function getGroupName( $group ) {
1864 $key = "group-$group-name";
1865 $name = wfMsg( $key );
1866 if( $name == '' ||
$name == "<$key>" ) {
1874 * Return the set of defined explicit groups.
1875 * The * and 'user' groups are not included.
1879 function getAllGroups() {
1880 global $wgGroupPermissions;
1882 array_keys( $wgGroupPermissions ),
1883 array( '*', 'user', 'autoconfirmed' ) );