02f7ec3d15fabdceb4b75dfbda827d4ea02c3fa7
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 );
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
41 /** Construct using User:loadDefaults() */
43 $this->loadDefaults();
44 $this->mVersion
= MW_USER_VERSION
;
48 * Static factory method
49 * @param string $name Username, validated by Title:newFromText()
53 function newFromName( $name ) {
54 # Force usernames to capital
56 $name = $wgContLang->ucfirst( $name );
58 # Clean up name according to title rules
59 $t = Title
::newFromText( $name );
64 # Reject various classes of invalid names
65 $canonicalName = $t->getText();
67 $canonicalName = $wgAuth->getCanonicalName( $t->getText() );
69 if( !User
::isValidUserName( $canonicalName ) ) {
74 $u->setName( $canonicalName );
75 $u->setId( $u->idFromName( $canonicalName ) );
80 * Factory method to fetch whichever use has a given email confirmation code.
81 * This code is generated when an account is created or its e-mail address
84 * If the code is invalid or has expired, returns NULL.
90 function newFromConfirmationCode( $code ) {
91 $dbr =& wfGetDB( DB_SLAVE
);
92 $name = $dbr->selectField( 'user', 'user_name', array(
93 'user_email_token' => md5( $code ),
94 'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
96 if( is_string( $name ) ) {
97 return User
::newFromName( $name );
104 * Serialze sleep function, for better cache efficiency and avoidance of
105 * silly "incomplete type" errors when skins are cached
108 return array( 'mId', 'mName', 'mPassword', 'mEmail', 'mNewtalk',
109 'mEmailAuthenticated', 'mRights', 'mOptions', 'mDataLoaded',
110 'mNewpassword', 'mBlockedby', 'mBlockreason', 'mTouched',
111 'mToken', 'mRealName', 'mHash', 'mGroups', 'mRegistration' );
115 * Get username given an id.
116 * @param integer $id Database user id
117 * @return string Nickname of a user
120 function whoIs( $id ) {
121 $dbr =& wfGetDB( DB_SLAVE
);
122 return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
126 * Get real username given an id.
127 * @param integer $id Database user id
128 * @return string Realname of a user
131 function whoIsReal( $id ) {
132 $dbr =& wfGetDB( DB_SLAVE
);
133 return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), 'User::whoIsReal' );
137 * Get database id given a user name
138 * @param string $name Nickname of a user
139 * @return integer|null Database user id (null: if non existent
142 function idFromName( $name ) {
143 $fname = "User::idFromName";
145 $nt = Title
::newFromText( $name );
146 if( is_null( $nt ) ) {
150 $dbr =& wfGetDB( DB_SLAVE
);
151 $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), $fname );
153 if ( $s === false ) {
161 * does the string match an anonymous IPv4 address?
163 * Note: We match \d{1,3}\.\d{1,3}\.\d{1,3}\.xxx as an anonymous IP
164 * address because the usemod software would "cloak" anonymous IP
165 * addresses like this, if we allowed accounts like this to be created
166 * new users could get the old edits of these anonymous users.
171 * @param string $name Nickname of a user
174 function isIP( $name ) {
175 return preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/",$name);
176 /*return preg_match("/^
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]))\.
180 (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))
185 * Is the input a valid username?
187 * Checks if the input is a valid username, we don't want an empty string,
188 * an IP address, anything that containins slashes (would mess up subpages),
189 * is longer than the maximum allowed username size or doesn't begin with
192 * @param string $name
196 function isValidUserName( $name ) {
197 global $wgContLang, $wgMaxNameChars;
200 || User
::isIP( $name )
201 ||
strpos( $name, '/' ) !== false
202 ||
strlen( $name ) > $wgMaxNameChars
203 ||
$name != $wgContLang->ucfirst( $name ) )
206 // Ensure that the name can't be misresolved as a different title,
207 // such as with extra namespace keys at the start.
208 $parsed = Title
::newFromText( $name );
209 if( is_null( $parsed )
210 ||
$parsed->getNamespace()
211 ||
strcmp( $name, $parsed->getPrefixedText() ) )
218 * Is the input a valid password?
220 * @param string $password
224 function isValidPassword( $password ) {
225 global $wgMinimalPasswordLength;
226 return strlen( $password ) >= $wgMinimalPasswordLength;
230 * Does the string match roughly an email address ?
232 * There used to be a regular expression here, it got removed because it
233 * rejected valid addresses. Actually just check if there is '@' somewhere
234 * in the given address.
236 * @todo Check for RFC 2822 compilance
239 * @param string $addr email address
243 function isValidEmailAddr ( $addr ) {
244 return ( trim( $addr ) != '' ) &&
245 (false !== strpos( $addr, '@' ) );
249 * Count the number of edits of a user
251 * @param int $uid The user ID to check
254 function edits( $uid ) {
255 $fname = 'User::edits';
257 $dbr =& wfGetDB( DB_SLAVE
);
258 return $dbr->selectField(
259 'revision', 'count(*)',
260 array( 'rev_user' => $uid ),
266 * probably return a random password
267 * @return string probably a random password
269 * @todo Check what is doing really [AV]
271 function randomPassword() {
272 global $wgMinimalPasswordLength;
273 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
274 $l = strlen( $pwchars ) - 1;
276 $pwlength = max( 7, $wgMinimalPasswordLength );
277 $digit = mt_rand(0, $pwlength - 1);
279 for ( $i = 0; $i < $pwlength; $i++
) {
280 $np .= $i == $digit ?
chr( mt_rand(48, 57) ) : $pwchars{ mt_rand(0, $l)};
286 * Set properties to default
287 * Used at construction. It will load per language default settings only
288 * if we have an available language object.
290 function loadDefaults() {
293 $fname = 'User::loadDefaults' . $n;
294 wfProfileIn( $fname );
296 global $wgContLang, $wgDBname;
297 global $wgNamespacesToBeSearchedDefault;
300 $this->mNewtalk
= -1;
301 $this->mName
= false;
302 $this->mRealName
= $this->mEmail
= '';
303 $this->mEmailAuthenticated
= null;
304 $this->mPassword
= $this->mNewpassword
= '';
305 $this->mRights
= array();
306 $this->mGroups
= array();
307 $this->mOptions
= User
::getDefaultOptions();
309 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
310 $this->mOptions
['searchNs'.$nsnum] = $val;
312 unset( $this->mSkin
);
313 $this->mDataLoaded
= false;
314 $this->mBlockedby
= -1; # Unset
315 $this->setToken(); # Random
316 $this->mHash
= false;
318 if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) {
319 $this->mTouched
= wfTimestamp( TS_MW
, $_COOKIE[$wgDBname.'LoggedOut'] );
322 $this->mTouched
= '0'; # Allow any pages to be cached
325 $this->mRegistration
= wfTimestamp( TS_MW
);
327 wfProfileOut( $fname );
331 * Combine the language default options with any site-specific options
332 * and add the default language variants.
338 function getDefaultOptions() {
340 * Site defaults will override the global/language defaults
342 global $wgContLang, $wgDefaultUserOptions;
343 $defOpt = $wgDefaultUserOptions +
$wgContLang->getDefaultUserOptions();
346 * default language setting
348 $variant = $wgContLang->getPreferredVariant();
349 $defOpt['variant'] = $variant;
350 $defOpt['language'] = $variant;
356 * Get a given default option value.
363 function getDefaultOption( $opt ) {
364 $defOpts = User
::getDefaultOptions();
365 if( isset( $defOpts[$opt] ) ) {
366 return $defOpts[$opt];
373 * Get blocking information
375 * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
376 * non-critical checks are done against slaves. Check when actually saving should be done against
379 function getBlockedStatus( $bFromSlave = true ) {
380 global $wgEnableSorbs, $wgProxyWhitelist;
382 if ( -1 != $this->mBlockedby
) {
383 wfDebug( "User::getBlockedStatus: already loaded.\n" );
387 $fname = 'User::getBlockedStatus';
388 wfProfileIn( $fname );
389 wfDebug( "$fname: checking...\n" );
391 $this->mBlockedby
= 0;
395 $block = new Block();
396 $block->fromMaster( !$bFromSlave );
397 if ( $block->load( $ip , $this->mId
) ) {
398 wfDebug( "$fname: Found block.\n" );
399 $this->mBlockedby
= $block->mBy
;
400 $this->mBlockreason
= $block->mReason
;
401 if ( $this->isLoggedIn() ) {
402 $this->spreadBlock();
405 wfDebug( "$fname: No block.\n" );
409 if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) {
412 if ( wfIsLocallyBlockedProxy( $ip ) ) {
413 $this->mBlockedby
= wfMsg( 'proxyblocker' );
414 $this->mBlockreason
= wfMsg( 'proxyblockreason' );
418 if ( !$this->mBlockedby
&& $wgEnableSorbs && !$this->getID() ) {
419 if ( $this->inSorbsBlacklist( $ip ) ) {
420 $this->mBlockedby
= wfMsg( 'sorbs' );
421 $this->mBlockreason
= wfMsg( 'sorbsreason' );
427 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
429 wfProfileOut( $fname );
432 function inSorbsBlacklist( $ip ) {
433 global $wgEnableSorbs;
434 return $wgEnableSorbs &&
435 $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
438 function inOpmBlacklist( $ip ) {
440 return $wgEnableOpm &&
441 $this->inDnsBlacklist( $ip, 'opm.blitzed.org.' );
444 function inDnsBlacklist( $ip, $base ) {
445 $fname = 'User::inDnsBlacklist';
446 wfProfileIn( $fname );
451 if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
453 for ( $i=4; $i>=1; $i-- ) {
454 $host .= $m[$i] . '.';
459 $ipList = gethostbynamel( $host );
462 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
465 wfDebug( "Requested $host, not found in $base.\n" );
469 wfProfileOut( $fname );
474 * Primitive rate limits: enforce maximum actions per time period
475 * to put a brake on flooding.
477 * Note: when using a shared cache like memcached, IP-address
478 * last-hit counters will be shared across wikis.
480 * @return bool true if a rate limiter was tripped
483 function pingLimiter( $action='edit' ) {
484 global $wgRateLimits;
485 if( !isset( $wgRateLimits[$action] ) ) {
488 if( $this->isAllowed( 'delete' ) ) {
493 global $wgMemc, $wgDBname, $wgRateLimitLog;
494 $fname = 'User::pingLimiter';
495 wfProfileIn( $fname );
497 $limits = $wgRateLimits[$action];
499 $id = $this->getId();
502 if( isset( $limits['anon'] ) && $id == 0 ) {
503 $keys["$wgDBname:limiter:$action:anon"] = $limits['anon'];
506 if( isset( $limits['user'] ) && $id != 0 ) {
507 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['user'];
509 if( $this->isNewbie() ) {
510 if( isset( $limits['newbie'] ) && $id != 0 ) {
511 $keys["$wgDBname:limiter:$action:user:$id"] = $limits['newbie'];
513 if( isset( $limits['ip'] ) ) {
514 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
516 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
517 $subnet = $matches[1];
518 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
523 foreach( $keys as $key => $limit ) {
524 list( $max, $period ) = $limit;
525 $summary = "(limit $max in {$period}s)";
526 $count = $wgMemc->get( $key );
528 if( $count > $max ) {
529 wfDebug( "$fname: tripped! $key at $count $summary\n" );
530 if( $wgRateLimitLog ) {
531 @error_log
( wfTimestamp( TS_MW
) . ' ' . $wgDBname . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
535 wfDebug( "$fname: ok. $key at $count $summary\n" );
538 wfDebug( "$fname: adding record for $key $summary\n" );
539 $wgMemc->add( $key, 1, intval( $period ) );
541 $wgMemc->incr( $key );
544 wfProfileOut( $fname );
549 * Check if user is blocked
550 * @return bool True if blocked, false otherwise
552 function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
553 wfDebug( "User::isBlocked: enter\n" );
554 $this->getBlockedStatus( $bFromSlave );
555 return $this->mBlockedby
!== 0;
559 * Check if user is blocked from editing a particular article
561 function isBlockedFrom( $title, $bFromSlave = false ) {
562 global $wgBlockAllowsUTEdit;
563 $fname = 'User::isBlockedFrom';
564 wfProfileIn( $fname );
565 wfDebug( "$fname: enter\n" );
567 if ( $wgBlockAllowsUTEdit && $title->getText() === $this->getName() &&
568 $title->getNamespace() == NS_USER_TALK
)
571 wfDebug( "$fname: self-talk page, ignoring any blocks\n" );
573 wfDebug( "$fname: asking isBlocked()\n" );
574 $blocked = $this->isBlocked( $bFromSlave );
576 wfProfileOut( $fname );
581 * Get name of blocker
582 * @return string name of blocker
584 function blockedBy() {
585 $this->getBlockedStatus();
586 return $this->mBlockedby
;
590 * Get blocking reason
591 * @return string Blocking reason
593 function blockedFor() {
594 $this->getBlockedStatus();
595 return $this->mBlockreason
;
599 * Initialise php session
601 function SetupSession() {
602 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
603 if( $wgSessionsInMemcached ) {
604 require_once( 'MemcachedSessions.php' );
605 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
606 # If it's left on 'user' or another setting from another
607 # application, it will end up failing. Try to recover.
608 ini_set ( 'session.save_handler', 'files' );
610 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
611 session_cache_limiter( 'private, must-revalidate' );
616 * Create a new user object using data from session
619 function loadFromSession() {
620 global $wgMemc, $wgDBname;
622 if ( isset( $_SESSION['wsUserID'] ) ) {
623 if ( 0 != $_SESSION['wsUserID'] ) {
624 $sId = $_SESSION['wsUserID'];
628 } else if ( isset( $_COOKIE["{$wgDBname}UserID"] ) ) {
629 $sId = intval( $_COOKIE["{$wgDBname}UserID"] );
630 $_SESSION['wsUserID'] = $sId;
634 if ( isset( $_SESSION['wsUserName'] ) ) {
635 $sName = $_SESSION['wsUserName'];
636 } else if ( isset( $_COOKIE["{$wgDBname}UserName"] ) ) {
637 $sName = $_COOKIE["{$wgDBname}UserName"];
638 $_SESSION['wsUserName'] = $sName;
643 $passwordCorrect = FALSE;
644 $user = $wgMemc->get( $key = "$wgDBname:user:id:$sId" );
645 if( !is_object( $user ) ||
$user->mVersion
< MW_USER_VERSION
) {
646 # Expire old serialized objects; they may be corrupt.
649 if($makenew = !$user) {
650 wfDebug( "User::loadFromSession() unable to load from memcached\n" );
653 $user->loadFromDatabase();
655 wfDebug( "User::loadFromSession() got from cache!\n" );
658 if ( isset( $_SESSION['wsToken'] ) ) {
659 $passwordCorrect = $_SESSION['wsToken'] == $user->mToken
;
660 } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) {
661 $passwordCorrect = $user->mToken
== $_COOKIE["{$wgDBname}Token"];
663 return new User(); # Can't log in from session
666 if ( ( $sName == $user->mName
) && $passwordCorrect ) {
668 if($wgMemc->set( $key, $user ))
669 wfDebug( "User::loadFromSession() successfully saved user\n" );
671 wfDebug( "User::loadFromSession() unable to save to memcached\n" );
675 return new User(); # Can't log in from session
679 * Load a user from the database
681 function loadFromDatabase() {
682 $fname = "User::loadFromDatabase";
684 # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress
685 # loading in a command line script, don't assume all command line scripts need it like this
686 #if ( $this->mDataLoaded || $wgCommandLineMode ) {
687 if ( $this->mDataLoaded
) {
692 $this->mId
= intval( $this->mId
);
694 /** Anonymous user */
697 $this->mRights
= $this->getGroupPermissions( array( '*' ) );
698 $this->mDataLoaded
= true;
700 } # the following stuff is for non-anonymous users only
702 $dbr =& wfGetDB( DB_SLAVE
);
703 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
704 'user_email_authenticated',
705 'user_real_name','user_options','user_touched', 'user_token', 'user_registration' ),
706 array( 'user_id' => $this->mId
), $fname );
708 if ( $s !== false ) {
709 $this->mName
= $s->user_name
;
710 $this->mEmail
= $s->user_email
;
711 $this->mEmailAuthenticated
= wfTimestampOrNull( TS_MW
, $s->user_email_authenticated
);
712 $this->mRealName
= $s->user_real_name
;
713 $this->mPassword
= $s->user_password
;
714 $this->mNewpassword
= $s->user_newpassword
;
715 $this->decodeOptions( $s->user_options
);
716 $this->mTouched
= wfTimestamp(TS_MW
,$s->user_touched
);
717 $this->mToken
= $s->user_token
;
718 $this->mRegistration
= wfTimestampOrNull( TS_MW
, $s->user_registration
);
720 $res = $dbr->select( 'user_groups',
722 array( 'ug_user' => $this->mId
),
724 $this->mGroups
= array();
725 while( $row = $dbr->fetchObject( $res ) ) {
726 $this->mGroups
[] = $row->ug_group
;
728 $implicitGroups = array( '*', 'user' );
730 global $wgAutoConfirmAge;
731 $accountAge = time() - wfTimestampOrNull( TS_UNIX
, $this->mRegistration
);
732 if( $accountAge >= $wgAutoConfirmAge ) {
733 $implicitGroups[] = 'autoconfirmed';
736 $effectiveGroups = array_merge( $implicitGroups, $this->mGroups
);
737 $this->mRights
= $this->getGroupPermissions( $effectiveGroups );
740 $this->mDataLoaded
= true;
743 function getID() { return $this->mId
; }
744 function setID( $v ) {
746 $this->mDataLoaded
= false;
750 $this->loadFromDatabase();
751 if ( $this->mName
=== false ) {
752 $this->mName
= wfGetIP();
757 function setName( $str ) {
758 $this->loadFromDatabase();
764 * Return the title dbkey form of the name, for eg user pages.
768 function getTitleKey() {
769 return str_replace( ' ', '_', $this->getName() );
772 function getNewtalk() {
773 $this->loadFromDatabase();
775 # Load the newtalk status if it is unloaded (mNewtalk=-1)
776 if( $this->mNewtalk
=== -1 ) {
777 $this->mNewtalk
= false; # 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
= (bool)$newtalk;
788 $this->mNewtalk
= $this->checkNewtalk( 'user_ip', $this->getName() );
789 $wgMemc->set( $key, $this->mNewtalk
, time() ); // + 1800 );
792 $this->mNewtalk
= $this->checkNewtalk( 'user_id', $this->mId
);
796 return (bool)$this->mNewtalk
;
800 * Perform a user_newtalk check on current slaves; if the memcached data
801 * is funky we don't want newtalk state to get stuck on save, as that's
804 * @param string $field
809 function checkNewtalk( $field, $id ) {
810 $fname = 'User::checkNewtalk';
811 $dbr =& wfGetDB( DB_SLAVE
);
812 $ok = $dbr->selectField( 'user_newtalk', $field,
813 array( $field => $id ), $fname );
814 return $ok !== false;
819 * @param string $field
823 function updateNewtalk( $field, $id ) {
824 $fname = 'User::updateNewtalk';
825 if( $this->checkNewtalk( $field, $id ) ) {
826 wfDebug( "$fname already set ($field, $id), ignoring\n" );
829 $dbw =& wfGetDB( DB_MASTER
);
830 $dbw->insert( 'user_newtalk',
831 array( $field => $id ),
834 wfDebug( "$fname: set on ($field, $id)\n" );
839 * Clear the new messages flag for the given user
840 * @param string $field
844 function deleteNewtalk( $field, $id ) {
845 $fname = 'User::deleteNewtalk';
846 if( !$this->checkNewtalk( $field, $id ) ) {
847 wfDebug( "$fname: already gone ($field, $id), ignoring\n" );
850 $dbw =& wfGetDB( DB_MASTER
);
851 $dbw->delete( 'user_newtalk',
852 array( $field => $id ),
854 wfDebug( "$fname: killed on ($field, $id)\n" );
859 * Update the 'You have new messages!' status.
862 function setNewtalk( $val ) {
867 $this->loadFromDatabase();
868 $this->mNewtalk
= $val;
870 $fname = 'User::setNewtalk';
872 if( $this->isAnon() ) {
874 $id = $this->getName();
877 $id = $this->getId();
881 $changed = $this->updateNewtalk( $field, $id );
883 $changed = $this->deleteNewtalk( $field, $id );
887 if( $this->isAnon() ) {
888 // Anons have a separate memcached space, since
889 // user records aren't kept for them.
890 global $wgDBname, $wgMemc;
891 $key = "$wgDBname:newtalk:ip:$val";
892 $wgMemc->set( $key, $val ?
1 : 0 );
895 // Make sure the user page is watched, so a notification
896 // will be sent out if enabled.
897 $this->addWatch( $this->getTalkPage() );
900 $this->invalidateCache();
901 $this->saveSettings();
905 function invalidateCache() {
906 global $wgClockSkewFudge;
907 $this->loadFromDatabase();
908 $this->mTouched
= wfTimestamp(TS_MW
, time() +
$wgClockSkewFudge );
909 # Don't forget to save the options after this or
910 # it won't take effect!
913 function validateCache( $timestamp ) {
914 $this->loadFromDatabase();
915 return ($timestamp >= $this->mTouched
);
919 * Encrypt a password.
920 * It can eventuall salt a password @see User::addSalt()
921 * @param string $p clear Password.
922 * @return string Encrypted password.
924 function encryptPassword( $p ) {
925 return wfEncryptPassword( $this->mId
, $p );
928 # Set the password and reset the random token
929 function setPassword( $str ) {
930 $this->loadFromDatabase();
932 $this->mPassword
= $this->encryptPassword( $str );
933 $this->mNewpassword
= '';
936 # Set the random token (used for persistent authentication)
937 function setToken( $token = false ) {
938 global $wgSecretKey, $wgProxyKey, $wgDBname;
940 if ( $wgSecretKey ) {
942 } elseif ( $wgProxyKey ) {
947 $this->mToken
= md5( $key . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId
);
949 $this->mToken
= $token;
954 function setCookiePassword( $str ) {
955 $this->loadFromDatabase();
956 $this->mCookiePassword
= md5( $str );
959 function setNewpassword( $str ) {
960 $this->loadFromDatabase();
961 $this->mNewpassword
= $this->encryptPassword( $str );
964 function getEmail() {
965 $this->loadFromDatabase();
966 return $this->mEmail
;
969 function getEmailAuthenticationTimestamp() {
970 $this->loadFromDatabase();
971 return $this->mEmailAuthenticated
;
974 function setEmail( $str ) {
975 $this->loadFromDatabase();
976 $this->mEmail
= $str;
979 function getRealName() {
980 $this->loadFromDatabase();
981 return $this->mRealName
;
984 function setRealName( $str ) {
985 $this->loadFromDatabase();
986 $this->mRealName
= $str;
990 * @param string $oname The option to check
993 function getOption( $oname ) {
994 $this->loadFromDatabase();
995 if ( array_key_exists( $oname, $this->mOptions
) ) {
996 return trim( $this->mOptions
[$oname] );
1003 * @param string $oname The option to check
1004 * @return bool False if the option is not selected, true if it is
1006 function getBoolOption( $oname ) {
1007 return (bool)$this->getOption( $oname );
1010 function setOption( $oname, $val ) {
1011 $this->loadFromDatabase();
1012 if ( $oname == 'skin' ) {
1013 # Clear cached skin, so the new one displays immediately in Special:Preferences
1014 unset( $this->mSkin
);
1016 $this->mOptions
[$oname] = $val;
1017 $this->invalidateCache();
1020 function getRights() {
1021 $this->loadFromDatabase();
1022 return $this->mRights
;
1026 * Get the list of explicit group memberships this user has.
1027 * The implicit * and user groups are not included.
1028 * @return array of strings
1030 function getGroups() {
1031 $this->loadFromDatabase();
1032 return $this->mGroups
;
1036 * Get the list of implicit group memberships this user has.
1037 * This includes all explicit groups, plus 'user' if logged in
1038 * and '*' for all accounts.
1039 * @return array of strings
1041 function getEffectiveGroups() {
1042 $base = array( '*' );
1043 if( $this->isLoggedIn() ) {
1046 return array_merge( $base, $this->getGroups() );
1050 * Add the user to the given group.
1051 * This takes immediate effect.
1054 function addGroup( $group ) {
1055 $dbw =& wfGetDB( DB_MASTER
);
1056 $dbw->insert( 'user_groups',
1058 'ug_user' => $this->getID(),
1059 'ug_group' => $group,
1062 array( 'IGNORE' ) );
1064 $this->mGroups
= array_merge( $this->mGroups
, array( $group ) );
1065 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1067 $this->invalidateCache();
1068 $this->saveSettings();
1072 * Remove the user from the given group.
1073 * This takes immediate effect.
1076 function removeGroup( $group ) {
1077 $dbw =& wfGetDB( DB_MASTER
);
1078 $dbw->delete( 'user_groups',
1080 'ug_user' => $this->getID(),
1081 'ug_group' => $group,
1083 'User::removeGroup' );
1085 $this->mGroups
= array_diff( $this->mGroups
, array( $group ) );
1086 $this->mRights
= User
::getGroupPermissions( $this->getEffectiveGroups() );
1088 $this->invalidateCache();
1089 $this->saveSettings();
1094 * A more legible check for non-anonymousness.
1095 * Returns true if the user is not an anonymous visitor.
1099 function isLoggedIn() {
1100 return( $this->getID() != 0 );
1104 * A more legible check for anonymousness.
1105 * Returns true if the user is an anonymous visitor.
1110 return !$this->isLoggedIn();
1114 * Check if a user is sysop
1117 function isSysop() {
1118 return $this->isAllowed( 'protect' );
1122 function isDeveloper() {
1123 return $this->isAllowed( 'siteadmin' );
1127 function isBureaucrat() {
1128 return $this->isAllowed( 'makesysop' );
1132 * Whether the user is a bot
1133 * @todo need to be migrated to the new user level management sytem
1136 $this->loadFromDatabase();
1137 return in_array( 'bot', $this->mRights
);
1141 * Check if user is allowed to access a feature / make an action
1142 * @param string $action Action to be checked (see $wgAvailableRights in Defines.php for possible actions).
1143 * @return boolean True: action is allowed, False: action should not be allowed
1145 function isAllowed($action='') {
1146 if ( $action === '' )
1147 // In the spirit of DWIM
1150 $this->loadFromDatabase();
1151 return in_array( $action , $this->mRights
);
1155 * Load a skin if it doesn't exist or return it
1156 * @todo FIXME : need to check the old failback system [AV]
1158 function &getSkin() {
1159 global $IP, $wgRequest;
1160 if ( ! isset( $this->mSkin
) ) {
1161 $fname = 'User::getSkin';
1162 wfProfileIn( $fname );
1165 $userSkin = $this->getOption( 'skin' );
1166 $userSkin = $wgRequest->getVal('useskin', $userSkin);
1168 $this->mSkin
=& Skin
::newFromKey( $userSkin );
1169 wfProfileOut( $fname );
1171 return $this->mSkin
;
1175 * @param string $title Article title to look at
1179 * Check watched status of an article
1180 * @return bool True if article is watched
1182 function isWatched( $title ) {
1183 $wl = WatchedItem
::fromUserTitle( $this, $title );
1184 return $wl->isWatched();
1190 function addWatch( $title ) {
1191 $wl = WatchedItem
::fromUserTitle( $this, $title );
1193 $this->invalidateCache();
1197 * Stop watching an article
1199 function removeWatch( $title ) {
1200 $wl = WatchedItem
::fromUserTitle( $this, $title );
1202 $this->invalidateCache();
1206 * Clear the user's notification timestamp for the given title.
1207 * If e-notif e-mails are on, they will receive notification mails on
1208 * the next change of the page if it's watched etc.
1210 function clearNotification( &$title ) {
1211 global $wgUser, $wgUseEnotif;
1213 if ($title->getNamespace() == NS_USER_TALK
&&
1214 $title->getText() == $this->getName() ) {
1215 $this->setNewtalk( false );
1218 if( !$wgUseEnotif ) {
1222 if( $this->isAnon() ) {
1223 // Nothing else to do...
1227 // Only update the timestamp if the page is being watched.
1228 // The query to find out if it is watched is cached both in memcached and per-invocation,
1229 // and when it does have to be executed, it can be on a slave
1230 // If this is the user's newtalk page, we always update the timestamp
1231 if ($title->getNamespace() == NS_USER_TALK
&&
1232 $title->getText() == $wgUser->getName())
1235 } elseif ( $this->getID() == $wgUser->getID() ) {
1236 $watched = $title->userIsWatching();
1241 // If the page is watched by the user (or may be watched), update the timestamp on any
1242 // any matching rows
1244 $dbw =& wfGetDB( DB_MASTER
);
1245 $success = $dbw->update( 'watchlist',
1247 'wl_notificationtimestamp' => NULL
1248 ), array( /* WHERE */
1249 'wl_title' => $title->getDBkey(),
1250 'wl_namespace' => $title->getNamespace(),
1251 'wl_user' => $this->getID()
1252 ), 'User::clearLastVisited'
1260 * Resets all of the given user's page-change notification timestamps.
1261 * If e-notif e-mails are on, they will receive notification mails on
1262 * the next change of any watched page.
1264 * @param int $currentUser user ID number
1267 function clearAllNotifications( $currentUser ) {
1268 global $wgUseEnotif;
1269 if ( !$wgUseEnotif ) {
1270 $this->setNewtalk( false );
1273 if( $currentUser != 0 ) {
1275 $dbw =& wfGetDB( DB_MASTER
);
1276 $success = $dbw->update( 'watchlist',
1278 'wl_notificationtimestamp' => 0
1279 ), array( /* WHERE */
1280 'wl_user' => $currentUser
1281 ), 'UserMailer::clearAll'
1284 # we also need to clear here the "you have new message" notification for the own user_talk page
1285 # This is cleared one page view later in Article::viewUpdates();
1291 * @return string Encoding options
1293 function encodeOptions() {
1295 foreach ( $this->mOptions
as $oname => $oval ) {
1296 array_push( $a, $oname.'='.$oval );
1298 $s = implode( "\n", $a );
1305 function decodeOptions( $str ) {
1306 $a = explode( "\n", $str );
1307 foreach ( $a as $s ) {
1308 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
1309 $this->mOptions
[$m[1]] = $m[2];
1314 function setCookies() {
1315 global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgDBname;
1316 if ( 0 == $this->mId
) return;
1317 $this->loadFromDatabase();
1318 $exp = time() +
$wgCookieExpiration;
1320 $_SESSION['wsUserID'] = $this->mId
;
1321 setcookie( $wgDBname.'UserID', $this->mId
, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1323 $_SESSION['wsUserName'] = $this->getName();
1324 setcookie( $wgDBname.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1326 $_SESSION['wsToken'] = $this->mToken
;
1327 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
1328 setcookie( $wgDBname.'Token', $this->mToken
, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1330 setcookie( $wgDBname.'Token', '', time() - 3600 );
1336 * It will clean the session cookie
1339 global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgDBname;
1340 $this->loadDefaults();
1341 $this->setLoaded( true );
1343 $_SESSION['wsUserID'] = 0;
1345 setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1346 setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1348 # Remember when user logged out, to prevent seeing cached pages
1349 setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() +
86400, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
1353 * Save object settings into database
1355 function saveSettings() {
1356 global $wgMemc, $wgDBname, $wgUseEnotif;
1357 $fname = 'User::saveSettings';
1359 if ( wfReadOnly() ) { return; }
1360 if ( 0 == $this->mId
) { return; }
1362 $dbw =& wfGetDB( DB_MASTER
);
1363 $dbw->update( 'user',
1365 'user_name' => $this->mName
,
1366 'user_password' => $this->mPassword
,
1367 'user_newpassword' => $this->mNewpassword
,
1368 'user_real_name' => $this->mRealName
,
1369 'user_email' => $this->mEmail
,
1370 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1371 'user_options' => $this->encodeOptions(),
1372 'user_touched' => $dbw->timestamp($this->mTouched
),
1373 'user_token' => $this->mToken
1374 ), array( /* WHERE */
1375 'user_id' => $this->mId
1378 $wgMemc->delete( "$wgDBname:user:id:$this->mId" );
1383 * Checks if a user with the given name exists, returns the ID
1385 function idForName() {
1386 $fname = 'User::idForName';
1389 $s = trim( $this->getName() );
1390 if ( 0 == strcmp( '', $s ) ) return 0;
1392 $dbr =& wfGetDB( DB_SLAVE
);
1393 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), $fname );
1394 if ( $id === false ) {
1401 * Add user object to the database
1403 function addToDatabase() {
1404 $fname = 'User::addToDatabase';
1405 $dbw =& wfGetDB( DB_MASTER
);
1406 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
1407 $dbw->insert( 'user',
1409 'user_id' => $seqVal,
1410 'user_name' => $this->mName
,
1411 'user_password' => $this->mPassword
,
1412 'user_newpassword' => $this->mNewpassword
,
1413 'user_email' => $this->mEmail
,
1414 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated
),
1415 'user_real_name' => $this->mRealName
,
1416 'user_options' => $this->encodeOptions(),
1417 'user_token' => $this->mToken
,
1418 'user_registration' => $dbw->timestamp( $this->mRegistration
),
1421 $this->mId
= $dbw->insertId();
1424 function spreadBlock() {
1425 # If the (non-anonymous) user is blocked, this function will block any IP address
1426 # that they successfully log on from.
1427 $fname = 'User::spreadBlock';
1429 wfDebug( "User:spreadBlock()\n" );
1430 if ( $this->mId
== 0 ) {
1434 $userblock = Block
::newFromDB( '', $this->mId
);
1435 if ( !$userblock->isValid() ) {
1439 # Check if this IP address is already blocked
1440 $ipblock = Block
::newFromDB( wfGetIP() );
1441 if ( $ipblock->isValid() ) {
1442 # If the user is already blocked. Then check if the autoblock would
1443 # excede the user block. If it would excede, then do nothing, else
1444 # prolong block time
1445 if ($userblock->mExpiry
&&
1446 ($userblock->mExpiry
< Block
::getAutoblockExpiry($ipblock->mTimestamp
))) {
1449 # Just update the timestamp
1450 $ipblock->updateTimestamp();
1454 # Make a new block object with the desired properties
1455 wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
1456 $ipblock->mAddress
= wfGetIP();
1457 $ipblock->mUser
= 0;
1458 $ipblock->mBy
= $userblock->mBy
;
1459 $ipblock->mReason
= wfMsg( 'autoblocker', $this->getName(), $userblock->mReason
);
1460 $ipblock->mTimestamp
= wfTimestampNow();
1461 $ipblock->mAuto
= 1;
1462 # If the user is already blocked with an expiry date, we don't
1463 # want to pile on top of that!
1464 if($userblock->mExpiry
) {
1465 $ipblock->mExpiry
= min ( $userblock->mExpiry
, Block
::getAutoblockExpiry( $ipblock->mTimestamp
));
1467 $ipblock->mExpiry
= Block
::getAutoblockExpiry( $ipblock->mTimestamp
);
1476 * Generate a string which will be different for any combination of
1477 * user options which would produce different parser output.
1478 * This will be used as part of the hash key for the parser cache,
1479 * so users will the same options can share the same cached data
1482 * Extensions which require it should install 'PageRenderingHash' hook,
1483 * which will give them a chance to modify this key based on their own
1488 function getPageRenderingHash() {
1491 return $this->mHash
;
1494 // stubthreshold is only included below for completeness,
1495 // it will always be 0 when this function is called by parsercache.
1497 $confstr = $this->getOption( 'math' );
1498 $confstr .= '!' . $this->getOption( 'stubthreshold' );
1499 $confstr .= '!' . $this->getOption( 'date' );
1500 $confstr .= '!' . ($this->getOption( 'numberheadings' ) ?
'1' : '');
1501 $confstr .= '!' . $this->getOption( 'language' );
1502 $confstr .= '!' . $this->getOption( 'thumbsize' );
1503 // add in language specific options, if any
1504 $extra = $wgContLang->getExtraHashOptions();
1507 // Give a chance for extensions to modify the hash, if they have
1508 // extra options or other effects on the parser cache.
1509 wfRunHooks( 'PageRenderingHash', array( &$confstr ) );
1511 $this->mHash
= $confstr;
1515 function isAllowedToCreateAccount() {
1516 return $this->isAllowed( 'createaccount' ) && !$this->isBlocked();
1520 * Set mDataLoaded, return previous value
1521 * Use this to prevent DB access in command-line scripts or similar situations
1523 function setLoaded( $loaded ) {
1524 return wfSetVar( $this->mDataLoaded
, $loaded );
1528 * Get this user's personal page title.
1533 function getUserPage() {
1534 return Title
::makeTitle( NS_USER
, $this->getName() );
1538 * Get this user's talk page title.
1543 function getTalkPage() {
1544 $title = $this->getUserPage();
1545 return $title->getTalkPage();
1551 function getMaxID() {
1552 static $res; // cache
1554 if ( isset( $res ) )
1557 $dbr =& wfGetDB( DB_SLAVE
);
1558 return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
1563 * Determine whether the user is a newbie. Newbies are either
1564 * anonymous IPs, or the most recently created accounts.
1565 * @return bool True if it is a newbie.
1567 function isNewbie() {
1568 return !$this->isAllowed( 'autoconfirmed' );
1572 * Check to see if the given clear-text password is one of the accepted passwords
1573 * @param string $password User password.
1574 * @return bool True if the given password is correct otherwise False.
1576 function checkPassword( $password ) {
1577 global $wgAuth, $wgMinimalPasswordLength;
1578 $this->loadFromDatabase();
1580 // Even though we stop people from creating passwords that
1581 // are shorter than this, doesn't mean people wont be able
1582 // to. Certain authentication plugins do NOT want to save
1583 // domain passwords in a mysql database, so we should
1584 // check this (incase $wgAuth->strict() is false).
1585 if( strlen( $password ) < $wgMinimalPasswordLength ) {
1589 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
1591 } elseif( $wgAuth->strict() ) {
1592 /* Auth plugin doesn't allow local authentication */
1595 $ep = $this->encryptPassword( $password );
1596 if ( 0 == strcmp( $ep, $this->mPassword
) ) {
1598 } elseif ( ($this->mNewpassword
!= '') && (0 == strcmp( $ep, $this->mNewpassword
)) ) {
1600 } elseif ( function_exists( 'iconv' ) ) {
1601 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
1602 # Check for this with iconv
1603 $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
1604 if ( 0 == strcmp( $cp1252hash, $this->mPassword
) ) {
1612 * Initialize (if necessary) and return a session token value
1613 * which can be used in edit forms to show that the user's
1614 * login credentials aren't being hijacked with a foreign form
1617 * @param mixed $salt - Optional function-specific data for hash.
1618 * Use a string or an array of strings.
1622 function editToken( $salt = '' ) {
1623 if( !isset( $_SESSION['wsEditToken'] ) ) {
1624 $token = $this->generateToken();
1625 $_SESSION['wsEditToken'] = $token;
1627 $token = $_SESSION['wsEditToken'];
1629 if( is_array( $salt ) ) {
1630 $salt = implode( '|', $salt );
1632 return md5( $token . $salt );
1636 * Generate a hex-y looking random token for various uses.
1637 * Could be made more cryptographically sure if someone cares.
1640 function generateToken( $salt = '' ) {
1641 $token = dechex( mt_rand() ) . dechex( mt_rand() );
1642 return md5( $token . $salt );
1646 * Check given value against the token value stored in the session.
1647 * A match should confirm that the form was submitted from the
1648 * user's own login session, not a form submission from a third-party
1651 * @param string $val - the input value to compare
1652 * @param string $salt - Optional function-specific data for hash
1656 function matchEditToken( $val, $salt = '' ) {
1660 if ( !isset( $_SESSION['wsEditToken'] ) ) {
1661 $logfile = '/home/wikipedia/logs/session_debug/session.log';
1662 $mckey = memsess_key( session_id() );
1663 $uname = @posix_uname();
1664 $msg = "wsEditToken not set!\n" .
1665 'apache server=' . $uname['nodename'] . "\n" .
1666 'session_id = ' . session_id() . "\n" .
1667 '$_SESSION=' . var_export( $_SESSION, true ) . "\n" .
1668 '$_COOKIE=' . var_export( $_COOKIE, true ) . "\n" .
1669 "mc get($mckey) = " . var_export( $wgMemc->get( $mckey ), true ) . "\n\n\n";
1671 @error_log( $msg, 3, $logfile );
1674 return ( $val == $this->editToken( $salt ) );
1678 * Generate a new e-mail confirmation token and send a confirmation
1679 * mail to the user's given address.
1681 * @return mixed True on success, a WikiError object on failure.
1683 function sendConfirmationMail() {
1685 $url = $this->confirmationTokenUrl( $expiration );
1686 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
1687 wfMsg( 'confirmemail_body',
1691 $wgContLang->timeanddate( $expiration, false ) ) );
1695 * Send an e-mail to this user's account. Does not check for
1696 * confirmed status or validity.
1698 * @param string $subject
1699 * @param string $body
1700 * @param strong $from Optional from address; default $wgPasswordSender will be used otherwise.
1701 * @return mixed True on success, a WikiError object on failure.
1703 function sendMail( $subject, $body, $from = null ) {
1704 if( is_null( $from ) ) {
1705 global $wgPasswordSender;
1706 $from = $wgPasswordSender;
1709 require_once( 'UserMailer.php' );
1710 $to = new MailAddress( $this );
1711 $sender = new MailAddress( $from );
1712 $error = userMailer( $to, $sender, $subject, $body );
1714 if( $error == '' ) {
1717 return new WikiError( $error );
1722 * Generate, store, and return a new e-mail confirmation code.
1723 * A hash (unsalted since it's used as a key) is stored.
1724 * @param &$expiration mixed output: accepts the expiration time
1728 function confirmationToken( &$expiration ) {
1729 $fname = 'User::confirmationToken';
1732 $expires = $now +
7 * 24 * 60 * 60;
1733 $expiration = wfTimestamp( TS_MW
, $expires );
1735 $token = $this->generateToken( $this->mId
. $this->mEmail
. $expires );
1736 $hash = md5( $token );
1738 $dbw =& wfGetDB( DB_MASTER
);
1739 $dbw->update( 'user',
1740 array( 'user_email_token' => $hash,
1741 'user_email_token_expires' => $dbw->timestamp( $expires ) ),
1742 array( 'user_id' => $this->mId
),
1749 * Generate and store a new e-mail confirmation token, and return
1750 * the URL the user can use to confirm.
1751 * @param &$expiration mixed output: accepts the expiration time
1755 function confirmationTokenUrl( &$expiration ) {
1756 $token = $this->confirmationToken( $expiration );
1757 $title = Title
::makeTitle( NS_SPECIAL
, 'Confirmemail/' . $token );
1758 return $title->getFullUrl();
1762 * Mark the e-mail address confirmed and save.
1764 function confirmEmail() {
1765 $this->loadFromDatabase();
1766 $this->mEmailAuthenticated
= wfTimestampNow();
1767 $this->saveSettings();
1772 * Is this user allowed to send e-mails within limits of current
1773 * site configuration?
1776 function canSendEmail() {
1777 return $this->isEmailConfirmed();
1781 * Is this user allowed to receive e-mails within limits of current
1782 * site configuration?
1785 function canReceiveEmail() {
1786 return $this->canSendEmail() && !$this->getOption( 'disablemail' );
1790 * Is this user's e-mail address valid-looking and confirmed within
1791 * limits of the current site configuration?
1793 * If $wgEmailAuthentication is on, this may require the user to have
1794 * confirmed their address by returning a code or using a password
1795 * sent to the address from the wiki.
1799 function isEmailConfirmed() {
1800 global $wgEmailAuthentication;
1801 $this->loadFromDatabase();
1802 if( $this->isAnon() )
1804 if( !$this->isValidEmailAddr( $this->mEmail
) )
1806 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
1812 * @param array $groups list of groups
1813 * @return array list of permission key names for given groups combined
1816 function getGroupPermissions( $groups ) {
1817 global $wgGroupPermissions;
1819 foreach( $groups as $group ) {
1820 if( isset( $wgGroupPermissions[$group] ) ) {
1821 $rights = array_merge( $rights,
1822 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
1829 * @param string $group key name
1830 * @return string localized descriptive name, if provided
1833 function getGroupName( $group ) {
1834 $key = "group-$group-name";
1835 $name = wfMsg( $key );
1836 if( $name == '' ||
$name == "<$key>" ) {
1844 * Return the set of defined explicit groups.
1845 * The * and 'user' groups are not included.
1849 function getAllGroups() {
1850 global $wgGroupPermissions;
1852 array_keys( $wgGroupPermissions ),
1853 array( '*', 'user', 'autoconfirmed' ) );
1857 * Return the set of groups which are not marked "invisible"
1861 function getVisibleGroups() {
1862 global $wgGroupPermissions, $wgInvisibleGroups;
1863 return array_diff( User
::getAllGroups(), $wgInvisibleGroups );
1867 * Determine if a given group name is a valid, visible group
1868 * @param string name
1871 function isVisibleGroup( $group ) {
1872 global $wgGroupPermissions, $wgInvisibleGroups;
1873 return isset( $wgGroupPermissions[$group] ) && !in_array( $group, $wgInvisibleGroups );