Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
48 | class User implements IDBAccessObject { |
||
49 | /** |
||
50 | * @const int Number of characters in user_token field. |
||
51 | */ |
||
52 | const TOKEN_LENGTH = 32; |
||
53 | |||
54 | /** |
||
55 | * @const string An invalid value for user_token |
||
56 | */ |
||
57 | const INVALID_TOKEN = '*** INVALID ***'; |
||
58 | |||
59 | /** |
||
60 | * Global constant made accessible as class constants so that autoloader |
||
61 | * magic can be used. |
||
62 | * @deprecated since 1.27, use \MediaWiki\Session\Token::SUFFIX |
||
63 | */ |
||
64 | const EDIT_TOKEN_SUFFIX = EDIT_TOKEN_SUFFIX; |
||
65 | |||
66 | /** |
||
67 | * @const int Serialized record version. |
||
68 | */ |
||
69 | const VERSION = 10; |
||
70 | |||
71 | /** |
||
72 | * Exclude user options that are set to their default value. |
||
73 | * @since 1.25 |
||
74 | */ |
||
75 | const GETOPTIONS_EXCLUDE_DEFAULTS = 1; |
||
76 | |||
77 | /** |
||
78 | * @since 1.27 |
||
79 | */ |
||
80 | const CHECK_USER_RIGHTS = true; |
||
81 | |||
82 | /** |
||
83 | * @since 1.27 |
||
84 | */ |
||
85 | const IGNORE_USER_RIGHTS = false; |
||
86 | |||
87 | /** |
||
88 | * Array of Strings List of member variables which are saved to the |
||
89 | * shared cache (memcached). Any operation which changes the |
||
90 | * corresponding database fields must call a cache-clearing function. |
||
91 | * @showinitializer |
||
92 | */ |
||
93 | protected static $mCacheVars = [ |
||
94 | // user table |
||
95 | 'mId', |
||
96 | 'mName', |
||
97 | 'mRealName', |
||
98 | 'mEmail', |
||
99 | 'mTouched', |
||
100 | 'mToken', |
||
101 | 'mEmailAuthenticated', |
||
102 | 'mEmailToken', |
||
103 | 'mEmailTokenExpires', |
||
104 | 'mRegistration', |
||
105 | 'mEditCount', |
||
106 | // user_groups table |
||
107 | 'mGroups', |
||
108 | // user_properties table |
||
109 | 'mOptionOverrides', |
||
110 | ]; |
||
111 | |||
112 | /** |
||
113 | * Array of Strings Core rights. |
||
114 | * Each of these should have a corresponding message of the form |
||
115 | * "right-$right". |
||
116 | * @showinitializer |
||
117 | */ |
||
118 | protected static $mCoreRights = [ |
||
119 | 'apihighlimits', |
||
120 | 'applychangetags', |
||
121 | 'autoconfirmed', |
||
122 | 'autocreateaccount', |
||
123 | 'autopatrol', |
||
124 | 'bigdelete', |
||
125 | 'block', |
||
126 | 'blockemail', |
||
127 | 'bot', |
||
128 | 'browsearchive', |
||
129 | 'changetags', |
||
130 | 'createaccount', |
||
131 | 'createpage', |
||
132 | 'createtalk', |
||
133 | 'delete', |
||
134 | 'deletechangetags', |
||
135 | 'deletedhistory', |
||
136 | 'deletedtext', |
||
137 | 'deletelogentry', |
||
138 | 'deleterevision', |
||
139 | 'edit', |
||
140 | 'editcontentmodel', |
||
141 | 'editinterface', |
||
142 | 'editprotected', |
||
143 | 'editmyoptions', |
||
144 | 'editmyprivateinfo', |
||
145 | 'editmyusercss', |
||
146 | 'editmyuserjs', |
||
147 | 'editmywatchlist', |
||
148 | 'editsemiprotected', |
||
149 | 'editusercssjs', # deprecated |
||
150 | 'editusercss', |
||
151 | 'edituserjs', |
||
152 | 'hideuser', |
||
153 | 'import', |
||
154 | 'importupload', |
||
155 | 'ipblock-exempt', |
||
156 | 'managechangetags', |
||
157 | 'markbotedits', |
||
158 | 'mergehistory', |
||
159 | 'minoredit', |
||
160 | 'move', |
||
161 | 'movefile', |
||
162 | 'move-categorypages', |
||
163 | 'move-rootuserpages', |
||
164 | 'move-subpages', |
||
165 | 'nominornewtalk', |
||
166 | 'noratelimit', |
||
167 | 'override-export-depth', |
||
168 | 'pagelang', |
||
169 | 'passwordreset', |
||
170 | 'patrol', |
||
171 | 'patrolmarks', |
||
172 | 'protect', |
||
173 | 'purge', |
||
174 | 'read', |
||
175 | 'reupload', |
||
176 | 'reupload-own', |
||
177 | 'reupload-shared', |
||
178 | 'rollback', |
||
179 | 'sendemail', |
||
180 | 'siteadmin', |
||
181 | 'suppressionlog', |
||
182 | 'suppressredirect', |
||
183 | 'suppressrevision', |
||
184 | 'unblockself', |
||
185 | 'undelete', |
||
186 | 'unwatchedpages', |
||
187 | 'upload', |
||
188 | 'upload_by_url', |
||
189 | 'userrights', |
||
190 | 'userrights-interwiki', |
||
191 | 'viewmyprivateinfo', |
||
192 | 'viewmywatchlist', |
||
193 | 'viewsuppressed', |
||
194 | 'writeapi', |
||
195 | ]; |
||
196 | |||
197 | /** |
||
198 | * String Cached results of getAllRights() |
||
199 | */ |
||
200 | protected static $mAllRights = false; |
||
201 | |||
202 | /** Cache variables */ |
||
203 | // @{ |
||
204 | /** @var int */ |
||
205 | public $mId; |
||
206 | /** @var string */ |
||
207 | public $mName; |
||
208 | /** @var string */ |
||
209 | public $mRealName; |
||
210 | |||
211 | /** @var string */ |
||
212 | public $mEmail; |
||
213 | /** @var string TS_MW timestamp from the DB */ |
||
214 | public $mTouched; |
||
215 | /** @var string TS_MW timestamp from cache */ |
||
216 | protected $mQuickTouched; |
||
217 | /** @var string */ |
||
218 | protected $mToken; |
||
219 | /** @var string */ |
||
220 | public $mEmailAuthenticated; |
||
221 | /** @var string */ |
||
222 | protected $mEmailToken; |
||
223 | /** @var string */ |
||
224 | protected $mEmailTokenExpires; |
||
225 | /** @var string */ |
||
226 | protected $mRegistration; |
||
227 | /** @var int */ |
||
228 | protected $mEditCount; |
||
229 | /** @var array */ |
||
230 | public $mGroups; |
||
231 | /** @var array */ |
||
232 | protected $mOptionOverrides; |
||
233 | // @} |
||
234 | |||
235 | /** |
||
236 | * Bool Whether the cache variables have been loaded. |
||
237 | */ |
||
238 | // @{ |
||
239 | public $mOptionsLoaded; |
||
240 | |||
241 | /** |
||
242 | * Array with already loaded items or true if all items have been loaded. |
||
243 | */ |
||
244 | protected $mLoadedItems = []; |
||
245 | // @} |
||
246 | |||
247 | /** |
||
248 | * String Initialization data source if mLoadedItems!==true. May be one of: |
||
249 | * - 'defaults' anonymous user initialised from class defaults |
||
250 | * - 'name' initialise from mName |
||
251 | * - 'id' initialise from mId |
||
252 | * - 'session' log in from session if possible |
||
253 | * |
||
254 | * Use the User::newFrom*() family of functions to set this. |
||
255 | */ |
||
256 | public $mFrom; |
||
257 | |||
258 | /** |
||
259 | * Lazy-initialized variables, invalidated with clearInstanceCache |
||
260 | */ |
||
261 | protected $mNewtalk; |
||
262 | /** @var string */ |
||
263 | protected $mDatePreference; |
||
264 | /** @var string */ |
||
265 | public $mBlockedby; |
||
266 | /** @var string */ |
||
267 | protected $mHash; |
||
268 | /** @var array */ |
||
269 | public $mRights; |
||
270 | /** @var string */ |
||
271 | protected $mBlockreason; |
||
272 | /** @var array */ |
||
273 | protected $mEffectiveGroups; |
||
274 | /** @var array */ |
||
275 | protected $mImplicitGroups; |
||
276 | /** @var array */ |
||
277 | protected $mFormerGroups; |
||
278 | /** @var Block */ |
||
279 | protected $mGlobalBlock; |
||
280 | /** @var bool */ |
||
281 | protected $mLocked; |
||
282 | /** @var bool */ |
||
283 | public $mHideName; |
||
284 | /** @var array */ |
||
285 | public $mOptions; |
||
286 | |||
287 | /** |
||
288 | * @var WebRequest |
||
289 | */ |
||
290 | private $mRequest; |
||
291 | |||
292 | /** @var Block */ |
||
293 | public $mBlock; |
||
294 | |||
295 | /** @var bool */ |
||
296 | protected $mAllowUsertalk; |
||
297 | |||
298 | /** @var Block */ |
||
299 | private $mBlockedFromCreateAccount = false; |
||
300 | |||
301 | /** @var integer User::READ_* constant bitfield used to load data */ |
||
302 | protected $queryFlagsUsed = self::READ_NORMAL; |
||
303 | |||
304 | public static $idCacheByName = []; |
||
305 | |||
306 | /** |
||
307 | * Lightweight constructor for an anonymous user. |
||
308 | * Use the User::newFrom* factory functions for other kinds of users. |
||
309 | * |
||
310 | * @see newFromName() |
||
311 | * @see newFromId() |
||
312 | * @see newFromConfirmationCode() |
||
313 | * @see newFromSession() |
||
314 | * @see newFromRow() |
||
315 | */ |
||
316 | public function __construct() { |
||
317 | $this->clearInstanceCache( 'defaults' ); |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @return string |
||
322 | */ |
||
323 | public function __toString() { |
||
324 | return (string)$this->getName(); |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Test if it's safe to load this User object. |
||
329 | * |
||
330 | * You should typically check this before using $wgUser or |
||
331 | * RequestContext::getUser in a method that might be called before the |
||
332 | * system has been fully initialized. If the object is unsafe, you should |
||
333 | * use an anonymous user: |
||
334 | * \code |
||
335 | * $user = $wgUser->isSafeToLoad() ? $wgUser : new User; |
||
336 | * \endcode |
||
337 | * |
||
338 | * @since 1.27 |
||
339 | * @return bool |
||
340 | */ |
||
341 | public function isSafeToLoad() { |
||
342 | global $wgFullyInitialised; |
||
343 | |||
344 | // The user is safe to load if: |
||
345 | // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data) |
||
346 | // * mLoadedItems === true (already loaded) |
||
347 | // * mFrom !== 'session' (sessions not involved at all) |
||
348 | |||
349 | return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) || |
||
350 | $this->mLoadedItems === true || $this->mFrom !== 'session'; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Load the user table data for this object from the source given by mFrom. |
||
355 | * |
||
356 | * @param integer $flags User::READ_* constant bitfield |
||
357 | */ |
||
358 | public function load( $flags = self::READ_NORMAL ) { |
||
359 | global $wgFullyInitialised; |
||
360 | |||
361 | if ( $this->mLoadedItems === true ) { |
||
362 | return; |
||
363 | } |
||
364 | |||
365 | // Set it now to avoid infinite recursion in accessors |
||
366 | $oldLoadedItems = $this->mLoadedItems; |
||
367 | $this->mLoadedItems = true; |
||
368 | $this->queryFlagsUsed = $flags; |
||
369 | |||
370 | // If this is called too early, things are likely to break. |
||
371 | if ( !$wgFullyInitialised && $this->mFrom === 'session' ) { |
||
372 | \MediaWiki\Logger\LoggerFactory::getInstance( 'session' ) |
||
373 | ->warning( 'User::loadFromSession called before the end of Setup.php', [ |
||
374 | 'exception' => new Exception( 'User::loadFromSession called before the end of Setup.php' ), |
||
375 | ] ); |
||
376 | $this->loadDefaults(); |
||
377 | $this->mLoadedItems = $oldLoadedItems; |
||
378 | return; |
||
379 | } |
||
380 | |||
381 | switch ( $this->mFrom ) { |
||
382 | case 'defaults': |
||
383 | $this->loadDefaults(); |
||
384 | break; |
||
385 | case 'name': |
||
386 | // Make sure this thread sees its own changes |
||
387 | if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) { |
||
388 | $flags |= self::READ_LATEST; |
||
389 | $this->queryFlagsUsed = $flags; |
||
390 | } |
||
391 | |||
392 | $this->mId = self::idFromName( $this->mName, $flags ); |
||
393 | if ( !$this->mId ) { |
||
394 | // Nonexistent user placeholder object |
||
395 | $this->loadDefaults( $this->mName ); |
||
396 | } else { |
||
397 | $this->loadFromId( $flags ); |
||
398 | } |
||
399 | break; |
||
400 | case 'id': |
||
401 | $this->loadFromId( $flags ); |
||
402 | break; |
||
403 | case 'session': |
||
404 | if ( !$this->loadFromSession() ) { |
||
405 | // Loading from session failed. Load defaults. |
||
406 | $this->loadDefaults(); |
||
407 | } |
||
408 | Hooks::run( 'UserLoadAfterLoadFromSession', [ $this ] ); |
||
409 | break; |
||
410 | default: |
||
411 | throw new UnexpectedValueException( |
||
412 | "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" ); |
||
413 | } |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Load user table data, given mId has already been set. |
||
418 | * @param integer $flags User::READ_* constant bitfield |
||
419 | * @return bool False if the ID does not exist, true otherwise |
||
420 | */ |
||
421 | public function loadFromId( $flags = self::READ_NORMAL ) { |
||
422 | if ( $this->mId == 0 ) { |
||
423 | // Anonymous users are not in the database (don't need cache) |
||
424 | $this->loadDefaults(); |
||
425 | return false; |
||
426 | } |
||
427 | |||
428 | // Try cache (unless this needs data from the master DB). |
||
429 | // NOTE: if this thread called saveSettings(), the cache was cleared. |
||
430 | $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ); |
||
431 | if ( $latest ) { |
||
432 | if ( !$this->loadFromDatabase( $flags ) ) { |
||
433 | // Can't load from ID |
||
434 | return false; |
||
435 | } |
||
436 | } else { |
||
437 | $this->loadFromCache(); |
||
438 | } |
||
439 | |||
440 | $this->mLoadedItems = true; |
||
441 | $this->queryFlagsUsed = $flags; |
||
442 | |||
443 | return true; |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * @since 1.27 |
||
448 | * @param string $wikiId |
||
449 | * @param integer $userId |
||
450 | */ |
||
451 | public static function purge( $wikiId, $userId ) { |
||
452 | $cache = ObjectCache::getMainWANInstance(); |
||
453 | $key = $cache->makeGlobalKey( 'user', 'id', $wikiId, $userId ); |
||
454 | $cache->delete( $key ); |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * @since 1.27 |
||
459 | * @param WANObjectCache $cache |
||
460 | * @return string |
||
461 | */ |
||
462 | protected function getCacheKey( WANObjectCache $cache ) { |
||
463 | return $cache->makeGlobalKey( 'user', 'id', wfWikiID(), $this->mId ); |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Load user data from shared cache, given mId has already been set. |
||
468 | * |
||
469 | * @return bool True |
||
470 | * @since 1.25 |
||
471 | */ |
||
472 | protected function loadFromCache() { |
||
473 | $cache = ObjectCache::getMainWANInstance(); |
||
474 | $data = $cache->getWithSetCallback( |
||
475 | $this->getCacheKey( $cache ), |
||
476 | $cache::TTL_HOUR, |
||
477 | function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) { |
||
478 | $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) ); |
||
479 | wfDebug( "User: cache miss for user {$this->mId}\n" ); |
||
480 | |||
481 | $this->loadFromDatabase( self::READ_NORMAL ); |
||
482 | $this->loadGroups(); |
||
483 | $this->loadOptions(); |
||
484 | |||
485 | $data = []; |
||
486 | foreach ( self::$mCacheVars as $name ) { |
||
487 | $data[$name] = $this->$name; |
||
488 | } |
||
489 | |||
490 | $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl ); |
||
491 | |||
492 | return $data; |
||
493 | |||
494 | }, |
||
495 | [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ] |
||
496 | ); |
||
497 | |||
498 | // Restore from cache |
||
499 | foreach ( self::$mCacheVars as $name ) { |
||
500 | $this->$name = $data[$name]; |
||
501 | } |
||
502 | |||
503 | return true; |
||
504 | } |
||
505 | |||
506 | /** @name newFrom*() static factory methods */ |
||
507 | // @{ |
||
508 | |||
509 | /** |
||
510 | * Static factory method for creation from username. |
||
511 | * |
||
512 | * This is slightly less efficient than newFromId(), so use newFromId() if |
||
513 | * you have both an ID and a name handy. |
||
514 | * |
||
515 | * @param string $name Username, validated by Title::newFromText() |
||
516 | * @param string|bool $validate Validate username. Takes the same parameters as |
||
517 | * User::getCanonicalName(), except that true is accepted as an alias |
||
518 | * for 'valid', for BC. |
||
519 | * |
||
520 | * @return User|bool User object, or false if the username is invalid |
||
521 | * (e.g. if it contains illegal characters or is an IP address). If the |
||
522 | * username is not present in the database, the result will be a user object |
||
523 | * with a name, zero user ID and default settings. |
||
524 | */ |
||
525 | public static function newFromName( $name, $validate = 'valid' ) { |
||
526 | if ( $validate === true ) { |
||
527 | $validate = 'valid'; |
||
528 | } |
||
529 | $name = self::getCanonicalName( $name, $validate ); |
||
530 | if ( $name === false ) { |
||
531 | return false; |
||
532 | } else { |
||
533 | // Create unloaded user object |
||
534 | $u = new User; |
||
535 | $u->mName = $name; |
||
536 | $u->mFrom = 'name'; |
||
537 | $u->setItemLoaded( 'name' ); |
||
538 | return $u; |
||
539 | } |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * Static factory method for creation from a given user ID. |
||
544 | * |
||
545 | * @param int $id Valid user ID |
||
546 | * @return User The corresponding User object |
||
547 | */ |
||
548 | public static function newFromId( $id ) { |
||
549 | $u = new User; |
||
550 | $u->mId = $id; |
||
551 | $u->mFrom = 'id'; |
||
552 | $u->setItemLoaded( 'id' ); |
||
553 | return $u; |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * Factory method to fetch whichever user has a given email confirmation code. |
||
558 | * This code is generated when an account is created or its e-mail address |
||
559 | * has changed. |
||
560 | * |
||
561 | * If the code is invalid or has expired, returns NULL. |
||
562 | * |
||
563 | * @param string $code Confirmation code |
||
564 | * @param int $flags User::READ_* bitfield |
||
565 | * @return User|null |
||
566 | */ |
||
567 | public static function newFromConfirmationCode( $code, $flags = 0 ) { |
||
568 | $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST |
||
569 | ? wfGetDB( DB_MASTER ) |
||
570 | : wfGetDB( DB_REPLICA ); |
||
571 | |||
572 | $id = $db->selectField( |
||
573 | 'user', |
||
574 | 'user_id', |
||
575 | [ |
||
576 | 'user_email_token' => md5( $code ), |
||
577 | 'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ), |
||
578 | ] |
||
579 | ); |
||
580 | |||
581 | return $id ? User::newFromId( $id ) : null; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Create a new user object using data from session. If the login |
||
586 | * credentials are invalid, the result is an anonymous user. |
||
587 | * |
||
588 | * @param WebRequest|null $request Object to use; $wgRequest will be used if omitted. |
||
589 | * @return User |
||
590 | */ |
||
591 | public static function newFromSession( WebRequest $request = null ) { |
||
592 | $user = new User; |
||
593 | $user->mFrom = 'session'; |
||
594 | $user->mRequest = $request; |
||
595 | return $user; |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * Create a new user object from a user row. |
||
600 | * The row should have the following fields from the user table in it: |
||
601 | * - either user_name or user_id to load further data if needed (or both) |
||
602 | * - user_real_name |
||
603 | * - all other fields (email, etc.) |
||
604 | * It is useless to provide the remaining fields if either user_id, |
||
605 | * user_name and user_real_name are not provided because the whole row |
||
606 | * will be loaded once more from the database when accessing them. |
||
607 | * |
||
608 | * @param stdClass $row A row from the user table |
||
609 | * @param array $data Further data to load into the object (see User::loadFromRow for valid keys) |
||
610 | * @return User |
||
611 | */ |
||
612 | public static function newFromRow( $row, $data = null ) { |
||
613 | $user = new User; |
||
614 | $user->loadFromRow( $row, $data ); |
||
615 | return $user; |
||
616 | } |
||
617 | |||
618 | /** |
||
619 | * Static factory method for creation of a "system" user from username. |
||
620 | * |
||
621 | * A "system" user is an account that's used to attribute logged actions |
||
622 | * taken by MediaWiki itself, as opposed to a bot or human user. Examples |
||
623 | * might include the 'Maintenance script' or 'Conversion script' accounts |
||
624 | * used by various scripts in the maintenance/ directory or accounts such |
||
625 | * as 'MediaWiki message delivery' used by the MassMessage extension. |
||
626 | * |
||
627 | * This can optionally create the user if it doesn't exist, and "steal" the |
||
628 | * account if it does exist. |
||
629 | * |
||
630 | * "Stealing" an existing user is intended to make it impossible for normal |
||
631 | * authentication processes to use the account, effectively disabling the |
||
632 | * account for normal use: |
||
633 | * - Email is invalidated, to prevent account recovery by emailing a |
||
634 | * temporary password and to disassociate the account from the existing |
||
635 | * human. |
||
636 | * - The token is set to a magic invalid value, to kill existing sessions |
||
637 | * and to prevent $this->setToken() calls from resetting the token to a |
||
638 | * valid value. |
||
639 | * - SessionManager is instructed to prevent new sessions for the user, to |
||
640 | * do things like deauthorizing OAuth consumers. |
||
641 | * - AuthManager is instructed to revoke access, to invalidate or remove |
||
642 | * passwords and other credentials. |
||
643 | * |
||
644 | * @param string $name Username |
||
645 | * @param array $options Options are: |
||
646 | * - validate: As for User::getCanonicalName(), default 'valid' |
||
647 | * - create: Whether to create the user if it doesn't already exist, default true |
||
648 | * - steal: Whether to "disable" the account for normal use if it already |
||
649 | * exists, default false |
||
650 | * @return User|null |
||
651 | * @since 1.27 |
||
652 | */ |
||
653 | public static function newSystemUser( $name, $options = [] ) { |
||
654 | $options += [ |
||
655 | 'validate' => 'valid', |
||
656 | 'create' => true, |
||
657 | 'steal' => false, |
||
658 | ]; |
||
659 | |||
660 | $name = self::getCanonicalName( $name, $options['validate'] ); |
||
661 | if ( $name === false ) { |
||
662 | return null; |
||
663 | } |
||
664 | |||
665 | $fields = self::selectFields(); |
||
666 | |||
667 | $dbw = wfGetDB( DB_MASTER ); |
||
668 | $row = $dbw->selectRow( |
||
669 | 'user', |
||
670 | $fields, |
||
671 | [ 'user_name' => $name ], |
||
672 | __METHOD__ |
||
673 | ); |
||
674 | if ( !$row ) { |
||
675 | // No user. Create it? |
||
676 | return $options['create'] ? self::createNew( $name ) : null; |
||
677 | } |
||
678 | $user = self::newFromRow( $row ); |
||
679 | |||
680 | // A user is considered to exist as a non-system user if it can |
||
681 | // authenticate, or has an email set, or has a non-invalid token. |
||
682 | if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN || |
||
683 | AuthManager::singleton()->userCanAuthenticate( $name ) |
||
684 | ) { |
||
685 | // User exists. Steal it? |
||
686 | if ( !$options['steal'] ) { |
||
687 | return null; |
||
688 | } |
||
689 | |||
690 | AuthManager::singleton()->revokeAccessForUser( $name ); |
||
691 | |||
692 | $user->invalidateEmail(); |
||
693 | $user->mToken = self::INVALID_TOKEN; |
||
694 | $user->saveSettings(); |
||
695 | SessionManager::singleton()->preventSessionsForUser( $user->getName() ); |
||
696 | } |
||
697 | |||
698 | return $user; |
||
699 | } |
||
700 | |||
701 | // @} |
||
702 | |||
703 | /** |
||
704 | * Get the username corresponding to a given user ID |
||
705 | * @param int $id User ID |
||
706 | * @return string|bool The corresponding username |
||
707 | */ |
||
708 | public static function whoIs( $id ) { |
||
709 | return UserCache::singleton()->getProp( $id, 'name' ); |
||
710 | } |
||
711 | |||
712 | /** |
||
713 | * Get the real name of a user given their user ID |
||
714 | * |
||
715 | * @param int $id User ID |
||
716 | * @return string|bool The corresponding user's real name |
||
717 | */ |
||
718 | public static function whoIsReal( $id ) { |
||
719 | return UserCache::singleton()->getProp( $id, 'real_name' ); |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Get database id given a user name |
||
724 | * @param string $name Username |
||
725 | * @param integer $flags User::READ_* constant bitfield |
||
726 | * @return int|null The corresponding user's ID, or null if user is nonexistent |
||
727 | */ |
||
728 | public static function idFromName( $name, $flags = self::READ_NORMAL ) { |
||
729 | $nt = Title::makeTitleSafe( NS_USER, $name ); |
||
730 | if ( is_null( $nt ) ) { |
||
731 | // Illegal name |
||
732 | return null; |
||
733 | } |
||
734 | |||
735 | if ( !( $flags & self::READ_LATEST ) && isset( self::$idCacheByName[$name] ) ) { |
||
736 | return self::$idCacheByName[$name]; |
||
737 | } |
||
738 | |||
739 | list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags ); |
||
740 | $db = wfGetDB( $index ); |
||
741 | |||
742 | $s = $db->selectRow( |
||
743 | 'user', |
||
744 | [ 'user_id' ], |
||
745 | [ 'user_name' => $nt->getText() ], |
||
746 | __METHOD__, |
||
747 | $options |
||
748 | ); |
||
749 | |||
750 | if ( $s === false ) { |
||
751 | $result = null; |
||
752 | } else { |
||
753 | $result = $s->user_id; |
||
754 | } |
||
755 | |||
756 | self::$idCacheByName[$name] = $result; |
||
757 | |||
758 | if ( count( self::$idCacheByName ) > 1000 ) { |
||
759 | self::$idCacheByName = []; |
||
760 | } |
||
761 | |||
762 | return $result; |
||
763 | } |
||
764 | |||
765 | /** |
||
766 | * Reset the cache used in idFromName(). For use in tests. |
||
767 | */ |
||
768 | public static function resetIdByNameCache() { |
||
769 | self::$idCacheByName = []; |
||
770 | } |
||
771 | |||
772 | /** |
||
773 | * Does the string match an anonymous IP address? |
||
774 | * |
||
775 | * This function exists for username validation, in order to reject |
||
776 | * usernames which are similar in form to IP addresses. Strings such |
||
777 | * as 300.300.300.300 will return true because it looks like an IP |
||
778 | * address, despite not being strictly valid. |
||
779 | * |
||
780 | * We match "\d{1,3}\.\d{1,3}\.\d{1,3}\.xxx" as an anonymous IP |
||
781 | * address because the usemod software would "cloak" anonymous IP |
||
782 | * addresses like this, if we allowed accounts like this to be created |
||
783 | * new users could get the old edits of these anonymous users. |
||
784 | * |
||
785 | * @param string $name Name to match |
||
786 | * @return bool |
||
787 | */ |
||
788 | public static function isIP( $name ) { |
||
789 | return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name ) |
||
790 | || IP::isIPv6( $name ); |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Is the input a valid username? |
||
795 | * |
||
796 | * Checks if the input is a valid username, we don't want an empty string, |
||
797 | * an IP address, anything that contains slashes (would mess up subpages), |
||
798 | * is longer than the maximum allowed username size or doesn't begin with |
||
799 | * a capital letter. |
||
800 | * |
||
801 | * @param string $name Name to match |
||
802 | * @return bool |
||
803 | */ |
||
804 | public static function isValidUserName( $name ) { |
||
805 | global $wgContLang, $wgMaxNameChars; |
||
806 | |||
807 | if ( $name == '' |
||
808 | || User::isIP( $name ) |
||
809 | || strpos( $name, '/' ) !== false |
||
810 | || strlen( $name ) > $wgMaxNameChars |
||
811 | || $name != $wgContLang->ucfirst( $name ) |
||
812 | ) { |
||
813 | return false; |
||
814 | } |
||
815 | |||
816 | // Ensure that the name can't be misresolved as a different title, |
||
817 | // such as with extra namespace keys at the start. |
||
818 | $parsed = Title::newFromText( $name ); |
||
819 | if ( is_null( $parsed ) |
||
820 | || $parsed->getNamespace() |
||
821 | || strcmp( $name, $parsed->getPrefixedText() ) ) { |
||
822 | return false; |
||
823 | } |
||
824 | |||
825 | // Check an additional blacklist of troublemaker characters. |
||
826 | // Should these be merged into the title char list? |
||
827 | $unicodeBlacklist = '/[' . |
||
828 | '\x{0080}-\x{009f}' . # iso-8859-1 control chars |
||
829 | '\x{00a0}' . # non-breaking space |
||
830 | '\x{2000}-\x{200f}' . # various whitespace |
||
831 | '\x{2028}-\x{202f}' . # breaks and control chars |
||
832 | '\x{3000}' . # ideographic space |
||
833 | '\x{e000}-\x{f8ff}' . # private use |
||
834 | ']/u'; |
||
835 | if ( preg_match( $unicodeBlacklist, $name ) ) { |
||
836 | return false; |
||
837 | } |
||
838 | |||
839 | return true; |
||
840 | } |
||
841 | |||
842 | /** |
||
843 | * Usernames which fail to pass this function will be blocked |
||
844 | * from user login and new account registrations, but may be used |
||
845 | * internally by batch processes. |
||
846 | * |
||
847 | * If an account already exists in this form, login will be blocked |
||
848 | * by a failure to pass this function. |
||
849 | * |
||
850 | * @param string $name Name to match |
||
851 | * @return bool |
||
852 | */ |
||
853 | public static function isUsableName( $name ) { |
||
854 | global $wgReservedUsernames; |
||
855 | // Must be a valid username, obviously ;) |
||
856 | if ( !self::isValidUserName( $name ) ) { |
||
857 | return false; |
||
858 | } |
||
859 | |||
860 | static $reservedUsernames = false; |
||
861 | if ( !$reservedUsernames ) { |
||
862 | $reservedUsernames = $wgReservedUsernames; |
||
863 | Hooks::run( 'UserGetReservedNames', [ &$reservedUsernames ] ); |
||
864 | } |
||
865 | |||
866 | // Certain names may be reserved for batch processes. |
||
867 | foreach ( $reservedUsernames as $reserved ) { |
||
868 | if ( substr( $reserved, 0, 4 ) == 'msg:' ) { |
||
869 | $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text(); |
||
870 | } |
||
871 | if ( $reserved == $name ) { |
||
872 | return false; |
||
873 | } |
||
874 | } |
||
875 | return true; |
||
876 | } |
||
877 | |||
878 | /** |
||
879 | * Return the users who are members of the given group(s). In case of multiple groups, |
||
880 | * users who are members of at least one of them are returned. |
||
881 | * |
||
882 | * @param string|array $groups A single group name or an array of group names |
||
883 | * @param int $limit Max number of users to return. The actual limit will never exceed 5000 |
||
884 | * records; larger values are ignored. |
||
885 | * @param int $after ID the user to start after |
||
886 | * @return UserArrayFromResult |
||
887 | */ |
||
888 | public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) { |
||
889 | if ( $groups === [] ) { |
||
890 | return UserArrayFromResult::newFromIDs( [] ); |
||
891 | } |
||
892 | |||
893 | $groups = array_unique( (array)$groups ); |
||
894 | $limit = min( 5000, $limit ); |
||
895 | |||
896 | $conds = [ 'ug_group' => $groups ]; |
||
897 | if ( $after !== null ) { |
||
898 | $conds[] = 'ug_user > ' . (int)$after; |
||
899 | } |
||
900 | |||
901 | $dbr = wfGetDB( DB_REPLICA ); |
||
902 | $ids = $dbr->selectFieldValues( |
||
903 | 'user_groups', |
||
904 | 'ug_user', |
||
905 | $conds, |
||
906 | __METHOD__, |
||
907 | [ |
||
908 | 'DISTINCT' => true, |
||
909 | 'ORDER BY' => 'ug_user', |
||
910 | 'LIMIT' => $limit, |
||
911 | ] |
||
912 | ) ?: []; |
||
913 | return UserArray::newFromIDs( $ids ); |
||
914 | } |
||
915 | |||
916 | /** |
||
917 | * Usernames which fail to pass this function will be blocked |
||
918 | * from new account registrations, but may be used internally |
||
919 | * either by batch processes or by user accounts which have |
||
920 | * already been created. |
||
921 | * |
||
922 | * Additional blacklisting may be added here rather than in |
||
923 | * isValidUserName() to avoid disrupting existing accounts. |
||
924 | * |
||
925 | * @param string $name String to match |
||
926 | * @return bool |
||
927 | */ |
||
928 | public static function isCreatableName( $name ) { |
||
929 | global $wgInvalidUsernameCharacters; |
||
930 | |||
931 | // Ensure that the username isn't longer than 235 bytes, so that |
||
932 | // (at least for the builtin skins) user javascript and css files |
||
933 | // will work. (bug 23080) |
||
934 | if ( strlen( $name ) > 235 ) { |
||
935 | wfDebugLog( 'username', __METHOD__ . |
||
936 | ": '$name' invalid due to length" ); |
||
937 | return false; |
||
938 | } |
||
939 | |||
940 | // Preg yells if you try to give it an empty string |
||
941 | if ( $wgInvalidUsernameCharacters !== '' ) { |
||
942 | if ( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) { |
||
943 | wfDebugLog( 'username', __METHOD__ . |
||
944 | ": '$name' invalid due to wgInvalidUsernameCharacters" ); |
||
945 | return false; |
||
946 | } |
||
947 | } |
||
948 | |||
949 | return self::isUsableName( $name ); |
||
950 | } |
||
951 | |||
952 | /** |
||
953 | * Is the input a valid password for this user? |
||
954 | * |
||
955 | * @param string $password Desired password |
||
956 | * @return bool |
||
957 | */ |
||
958 | public function isValidPassword( $password ) { |
||
959 | // simple boolean wrapper for getPasswordValidity |
||
960 | return $this->getPasswordValidity( $password ) === true; |
||
961 | } |
||
962 | |||
963 | /** |
||
964 | * Given unvalidated password input, return error message on failure. |
||
965 | * |
||
966 | * @param string $password Desired password |
||
967 | * @return bool|string|array True on success, string or array of error message on failure |
||
968 | */ |
||
969 | public function getPasswordValidity( $password ) { |
||
970 | $result = $this->checkPasswordValidity( $password ); |
||
971 | if ( $result->isGood() ) { |
||
972 | return true; |
||
973 | } else { |
||
974 | $messages = []; |
||
975 | foreach ( $result->getErrorsByType( 'error' ) as $error ) { |
||
976 | $messages[] = $error['message']; |
||
977 | } |
||
978 | foreach ( $result->getErrorsByType( 'warning' ) as $warning ) { |
||
979 | $messages[] = $warning['message']; |
||
980 | } |
||
981 | if ( count( $messages ) === 1 ) { |
||
982 | return $messages[0]; |
||
983 | } |
||
984 | return $messages; |
||
985 | } |
||
986 | } |
||
987 | |||
988 | /** |
||
989 | * Check if this is a valid password for this user |
||
990 | * |
||
991 | * Create a Status object based on the password's validity. |
||
992 | * The Status should be set to fatal if the user should not |
||
993 | * be allowed to log in, and should have any errors that |
||
994 | * would block changing the password. |
||
995 | * |
||
996 | * If the return value of this is not OK, the password |
||
997 | * should not be checked. If the return value is not Good, |
||
998 | * the password can be checked, but the user should not be |
||
999 | * able to set their password to this. |
||
1000 | * |
||
1001 | * @param string $password Desired password |
||
1002 | * @param string $purpose one of 'login', 'create', 'reset' |
||
1003 | * @return Status |
||
1004 | * @since 1.23 |
||
1005 | */ |
||
1006 | public function checkPasswordValidity( $password, $purpose = 'login' ) { |
||
1007 | global $wgPasswordPolicy; |
||
1008 | |||
1009 | $upp = new UserPasswordPolicy( |
||
1010 | $wgPasswordPolicy['policies'], |
||
1011 | $wgPasswordPolicy['checks'] |
||
1012 | ); |
||
1013 | |||
1014 | $status = Status::newGood(); |
||
1015 | $result = false; // init $result to false for the internal checks |
||
1016 | |||
1017 | if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) { |
||
1018 | $status->error( $result ); |
||
1019 | return $status; |
||
1020 | } |
||
1021 | |||
1022 | if ( $result === false ) { |
||
1023 | $status->merge( $upp->checkUserPassword( $this, $password, $purpose ) ); |
||
1024 | return $status; |
||
1025 | } elseif ( $result === true ) { |
||
1026 | return $status; |
||
1027 | } else { |
||
1028 | $status->error( $result ); |
||
1029 | return $status; // the isValidPassword hook set a string $result and returned true |
||
1030 | } |
||
1031 | } |
||
1032 | |||
1033 | /** |
||
1034 | * Given unvalidated user input, return a canonical username, or false if |
||
1035 | * the username is invalid. |
||
1036 | * @param string $name User input |
||
1037 | * @param string|bool $validate Type of validation to use: |
||
1038 | * - false No validation |
||
1039 | * - 'valid' Valid for batch processes |
||
1040 | * - 'usable' Valid for batch processes and login |
||
1041 | * - 'creatable' Valid for batch processes, login and account creation |
||
1042 | * |
||
1043 | * @throws InvalidArgumentException |
||
1044 | * @return bool|string |
||
1045 | */ |
||
1046 | public static function getCanonicalName( $name, $validate = 'valid' ) { |
||
1047 | // Force usernames to capital |
||
1048 | global $wgContLang; |
||
1049 | $name = $wgContLang->ucfirst( $name ); |
||
1050 | |||
1051 | # Reject names containing '#'; these will be cleaned up |
||
1052 | # with title normalisation, but then it's too late to |
||
1053 | # check elsewhere |
||
1054 | if ( strpos( $name, '#' ) !== false ) { |
||
1055 | return false; |
||
1056 | } |
||
1057 | |||
1058 | // Clean up name according to title rules, |
||
1059 | // but only when validation is requested (bug 12654) |
||
1060 | $t = ( $validate !== false ) ? |
||
1061 | Title::newFromText( $name, NS_USER ) : Title::makeTitle( NS_USER, $name ); |
||
1062 | // Check for invalid titles |
||
1063 | if ( is_null( $t ) || $t->getNamespace() !== NS_USER || $t->isExternal() ) { |
||
1064 | return false; |
||
1065 | } |
||
1066 | |||
1067 | // Reject various classes of invalid names |
||
1068 | $name = AuthManager::callLegacyAuthPlugin( |
||
1069 | 'getCanonicalName', [ $t->getText() ], $t->getText() |
||
1070 | ); |
||
1071 | |||
1072 | switch ( $validate ) { |
||
1073 | case false: |
||
1074 | break; |
||
1075 | case 'valid': |
||
1076 | if ( !User::isValidUserName( $name ) ) { |
||
1077 | $name = false; |
||
1078 | } |
||
1079 | break; |
||
1080 | case 'usable': |
||
1081 | if ( !User::isUsableName( $name ) ) { |
||
1082 | $name = false; |
||
1083 | } |
||
1084 | break; |
||
1085 | case 'creatable': |
||
1086 | if ( !User::isCreatableName( $name ) ) { |
||
1087 | $name = false; |
||
1088 | } |
||
1089 | break; |
||
1090 | default: |
||
1091 | throw new InvalidArgumentException( |
||
1092 | 'Invalid parameter value for $validate in ' . __METHOD__ ); |
||
1093 | } |
||
1094 | return $name; |
||
1095 | } |
||
1096 | |||
1097 | /** |
||
1098 | * Count the number of edits of a user |
||
1099 | * |
||
1100 | * @param int $uid User ID to check |
||
1101 | * @return int The user's edit count |
||
1102 | * |
||
1103 | * @deprecated since 1.21 in favour of User::getEditCount |
||
1104 | */ |
||
1105 | public static function edits( $uid ) { |
||
1106 | wfDeprecated( __METHOD__, '1.21' ); |
||
1107 | $user = self::newFromId( $uid ); |
||
1108 | return $user->getEditCount(); |
||
1109 | } |
||
1110 | |||
1111 | /** |
||
1112 | * Return a random password. |
||
1113 | * |
||
1114 | * @deprecated since 1.27, use PasswordFactory::generateRandomPasswordString() |
||
1115 | * @return string New random password |
||
1116 | */ |
||
1117 | public static function randomPassword() { |
||
1118 | global $wgMinimalPasswordLength; |
||
1119 | return PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength ); |
||
1120 | } |
||
1121 | |||
1122 | /** |
||
1123 | * Set cached properties to default. |
||
1124 | * |
||
1125 | * @note This no longer clears uncached lazy-initialised properties; |
||
1126 | * the constructor does that instead. |
||
1127 | * |
||
1128 | * @param string|bool $name |
||
1129 | */ |
||
1130 | public function loadDefaults( $name = false ) { |
||
1131 | $this->mId = 0; |
||
1132 | $this->mName = $name; |
||
1133 | $this->mRealName = ''; |
||
1134 | $this->mEmail = ''; |
||
1135 | $this->mOptionOverrides = null; |
||
1136 | $this->mOptionsLoaded = false; |
||
1137 | |||
1138 | $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' ) |
||
1139 | ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0; |
||
1140 | if ( $loggedOut !== 0 ) { |
||
1141 | $this->mTouched = wfTimestamp( TS_MW, $loggedOut ); |
||
1142 | } else { |
||
1143 | $this->mTouched = '1'; # Allow any pages to be cached |
||
1144 | } |
||
1145 | |||
1146 | $this->mToken = null; // Don't run cryptographic functions till we need a token |
||
1147 | $this->mEmailAuthenticated = null; |
||
1148 | $this->mEmailToken = ''; |
||
1149 | $this->mEmailTokenExpires = null; |
||
1150 | $this->mRegistration = wfTimestamp( TS_MW ); |
||
1151 | $this->mGroups = []; |
||
1152 | |||
1153 | Hooks::run( 'UserLoadDefaults', [ $this, $name ] ); |
||
1154 | } |
||
1155 | |||
1156 | /** |
||
1157 | * Return whether an item has been loaded. |
||
1158 | * |
||
1159 | * @param string $item Item to check. Current possibilities: |
||
1160 | * - id |
||
1161 | * - name |
||
1162 | * - realname |
||
1163 | * @param string $all 'all' to check if the whole object has been loaded |
||
1164 | * or any other string to check if only the item is available (e.g. |
||
1165 | * for optimisation) |
||
1166 | * @return bool |
||
1167 | */ |
||
1168 | public function isItemLoaded( $item, $all = 'all' ) { |
||
1169 | return ( $this->mLoadedItems === true && $all === 'all' ) || |
||
1170 | ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true ); |
||
1171 | } |
||
1172 | |||
1173 | /** |
||
1174 | * Set that an item has been loaded |
||
1175 | * |
||
1176 | * @param string $item |
||
1177 | */ |
||
1178 | protected function setItemLoaded( $item ) { |
||
1179 | if ( is_array( $this->mLoadedItems ) ) { |
||
1180 | $this->mLoadedItems[$item] = true; |
||
1181 | } |
||
1182 | } |
||
1183 | |||
1184 | /** |
||
1185 | * Load user data from the session. |
||
1186 | * |
||
1187 | * @return bool True if the user is logged in, false otherwise. |
||
1188 | */ |
||
1189 | private function loadFromSession() { |
||
1190 | // Deprecated hook |
||
1191 | $result = null; |
||
1192 | Hooks::run( 'UserLoadFromSession', [ $this, &$result ], '1.27' ); |
||
1193 | if ( $result !== null ) { |
||
1194 | return $result; |
||
1195 | } |
||
1196 | |||
1197 | // MediaWiki\Session\Session already did the necessary authentication of the user |
||
1198 | // returned here, so just use it if applicable. |
||
1199 | $session = $this->getRequest()->getSession(); |
||
1200 | $user = $session->getUser(); |
||
1201 | if ( $user->isLoggedIn() ) { |
||
1202 | $this->loadFromUserObject( $user ); |
||
1203 | // Other code expects these to be set in the session, so set them. |
||
1204 | $session->set( 'wsUserID', $this->getId() ); |
||
1205 | $session->set( 'wsUserName', $this->getName() ); |
||
1206 | $session->set( 'wsToken', $this->getToken() ); |
||
1207 | return true; |
||
1208 | } |
||
1209 | |||
1210 | return false; |
||
1211 | } |
||
1212 | |||
1213 | /** |
||
1214 | * Load user and user_group data from the database. |
||
1215 | * $this->mId must be set, this is how the user is identified. |
||
1216 | * |
||
1217 | * @param integer $flags User::READ_* constant bitfield |
||
1218 | * @return bool True if the user exists, false if the user is anonymous |
||
1219 | */ |
||
1220 | public function loadFromDatabase( $flags = self::READ_LATEST ) { |
||
1221 | // Paranoia |
||
1222 | $this->mId = intval( $this->mId ); |
||
1223 | |||
1224 | if ( !$this->mId ) { |
||
1225 | // Anonymous users are not in the database |
||
1226 | $this->loadDefaults(); |
||
1227 | return false; |
||
1228 | } |
||
1229 | |||
1230 | list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags ); |
||
1231 | $db = wfGetDB( $index ); |
||
1232 | |||
1233 | $s = $db->selectRow( |
||
1234 | 'user', |
||
1235 | self::selectFields(), |
||
1236 | [ 'user_id' => $this->mId ], |
||
1237 | __METHOD__, |
||
1238 | $options |
||
1239 | ); |
||
1240 | |||
1241 | $this->queryFlagsUsed = $flags; |
||
1242 | Hooks::run( 'UserLoadFromDatabase', [ $this, &$s ] ); |
||
1243 | |||
1244 | if ( $s !== false ) { |
||
1245 | // Initialise user table data |
||
1246 | $this->loadFromRow( $s ); |
||
1247 | $this->mGroups = null; // deferred |
||
1248 | $this->getEditCount(); // revalidation for nulls |
||
1249 | return true; |
||
1250 | } else { |
||
1251 | // Invalid user_id |
||
1252 | $this->mId = 0; |
||
1253 | $this->loadDefaults(); |
||
1254 | return false; |
||
1255 | } |
||
1256 | } |
||
1257 | |||
1258 | /** |
||
1259 | * Initialize this object from a row from the user table. |
||
1260 | * |
||
1261 | * @param stdClass $row Row from the user table to load. |
||
1262 | * @param array $data Further user data to load into the object |
||
1263 | * |
||
1264 | * user_groups Array with groups out of the user_groups table |
||
1265 | * user_properties Array with properties out of the user_properties table |
||
1266 | */ |
||
1267 | protected function loadFromRow( $row, $data = null ) { |
||
1268 | $all = true; |
||
1269 | |||
1270 | $this->mGroups = null; // deferred |
||
1271 | |||
1272 | View Code Duplication | if ( isset( $row->user_name ) ) { |
|
1273 | $this->mName = $row->user_name; |
||
1274 | $this->mFrom = 'name'; |
||
1275 | $this->setItemLoaded( 'name' ); |
||
1276 | } else { |
||
1277 | $all = false; |
||
1278 | } |
||
1279 | |||
1280 | if ( isset( $row->user_real_name ) ) { |
||
1281 | $this->mRealName = $row->user_real_name; |
||
1282 | $this->setItemLoaded( 'realname' ); |
||
1283 | } else { |
||
1284 | $all = false; |
||
1285 | } |
||
1286 | |||
1287 | View Code Duplication | if ( isset( $row->user_id ) ) { |
|
1288 | $this->mId = intval( $row->user_id ); |
||
1289 | $this->mFrom = 'id'; |
||
1290 | $this->setItemLoaded( 'id' ); |
||
1291 | } else { |
||
1292 | $all = false; |
||
1293 | } |
||
1294 | |||
1295 | if ( isset( $row->user_id ) && isset( $row->user_name ) ) { |
||
1296 | self::$idCacheByName[$row->user_name] = $row->user_id; |
||
1297 | } |
||
1298 | |||
1299 | if ( isset( $row->user_editcount ) ) { |
||
1300 | $this->mEditCount = $row->user_editcount; |
||
1301 | } else { |
||
1302 | $all = false; |
||
1303 | } |
||
1304 | |||
1305 | if ( isset( $row->user_touched ) ) { |
||
1306 | $this->mTouched = wfTimestamp( TS_MW, $row->user_touched ); |
||
1307 | } else { |
||
1308 | $all = false; |
||
1309 | } |
||
1310 | |||
1311 | if ( isset( $row->user_token ) ) { |
||
1312 | // The definition for the column is binary(32), so trim the NULs |
||
1313 | // that appends. The previous definition was char(32), so trim |
||
1314 | // spaces too. |
||
1315 | $this->mToken = rtrim( $row->user_token, " \0" ); |
||
1316 | if ( $this->mToken === '' ) { |
||
1317 | $this->mToken = null; |
||
1318 | } |
||
1319 | } else { |
||
1320 | $all = false; |
||
1321 | } |
||
1322 | |||
1323 | if ( isset( $row->user_email ) ) { |
||
1324 | $this->mEmail = $row->user_email; |
||
1325 | $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); |
||
1326 | $this->mEmailToken = $row->user_email_token; |
||
1327 | $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); |
||
1328 | $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration ); |
||
1329 | } else { |
||
1330 | $all = false; |
||
1331 | } |
||
1332 | |||
1333 | if ( $all ) { |
||
1334 | $this->mLoadedItems = true; |
||
1335 | } |
||
1336 | |||
1337 | if ( is_array( $data ) ) { |
||
1338 | if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) { |
||
1339 | $this->mGroups = $data['user_groups']; |
||
1340 | } |
||
1341 | if ( isset( $data['user_properties'] ) && is_array( $data['user_properties'] ) ) { |
||
1342 | $this->loadOptions( $data['user_properties'] ); |
||
1343 | } |
||
1344 | } |
||
1345 | } |
||
1346 | |||
1347 | /** |
||
1348 | * Load the data for this user object from another user object. |
||
1349 | * |
||
1350 | * @param User $user |
||
1351 | */ |
||
1352 | protected function loadFromUserObject( $user ) { |
||
1353 | $user->load(); |
||
1354 | foreach ( self::$mCacheVars as $var ) { |
||
1355 | $this->$var = $user->$var; |
||
1356 | } |
||
1357 | } |
||
1358 | |||
1359 | /** |
||
1360 | * Load the groups from the database if they aren't already loaded. |
||
1361 | */ |
||
1362 | View Code Duplication | private function loadGroups() { |
|
1377 | |||
1378 | /** |
||
1379 | * Add the user to the group if he/she meets given criteria. |
||
1380 | * |
||
1381 | * Contrary to autopromotion by \ref $wgAutopromote, the group will be |
||
1382 | * possible to remove manually via Special:UserRights. In such case it |
||
1383 | * will not be re-added automatically. The user will also not lose the |
||
1384 | * group if they no longer meet the criteria. |
||
1385 | * |
||
1386 | * @param string $event Key in $wgAutopromoteOnce (each one has groups/criteria) |
||
1387 | * |
||
1388 | * @return array Array of groups the user has been promoted to. |
||
1389 | * |
||
1390 | * @see $wgAutopromoteOnce |
||
1391 | */ |
||
1392 | public function addAutopromoteOnceGroups( $event ) { |
||
1432 | |||
1433 | /** |
||
1434 | * Builds update conditions. Additional conditions may be added to $conditions to |
||
1435 | * protected against race conditions using a compare-and-set (CAS) mechanism |
||
1436 | * based on comparing $this->mTouched with the user_touched field. |
||
1437 | * |
||
1438 | * @param Database $db |
||
1439 | * @param array $conditions WHERE conditions for use with Database::update |
||
1440 | * @return array WHERE conditions for use with Database::update |
||
1441 | */ |
||
1442 | protected function makeUpdateConditions( Database $db, array $conditions ) { |
||
1443 | if ( $this->mTouched ) { |
||
1444 | // CAS check: only update if the row wasn't changed sicne it was loaded. |
||
1445 | $conditions['user_touched'] = $db->timestamp( $this->mTouched ); |
||
1446 | } |
||
1447 | |||
1448 | return $conditions; |
||
1449 | } |
||
1450 | |||
1451 | /** |
||
1452 | * Bump user_touched if it didn't change since this object was loaded |
||
1453 | * |
||
1454 | * On success, the mTouched field is updated. |
||
1455 | * The user serialization cache is always cleared. |
||
1456 | * |
||
1457 | * @return bool Whether user_touched was actually updated |
||
1458 | * @since 1.26 |
||
1459 | */ |
||
1460 | protected function checkAndSetTouched() { |
||
1461 | $this->load(); |
||
1462 | |||
1463 | if ( !$this->mId ) { |
||
1464 | return false; // anon |
||
1465 | } |
||
1466 | |||
1467 | // Get a new user_touched that is higher than the old one |
||
1468 | $newTouched = $this->newTouchedTimestamp(); |
||
1469 | |||
1470 | $dbw = wfGetDB( DB_MASTER ); |
||
1471 | $dbw->update( 'user', |
||
1472 | [ 'user_touched' => $dbw->timestamp( $newTouched ) ], |
||
1473 | $this->makeUpdateConditions( $dbw, [ |
||
1474 | 'user_id' => $this->mId, |
||
1475 | ] ), |
||
1476 | __METHOD__ |
||
1477 | ); |
||
1478 | $success = ( $dbw->affectedRows() > 0 ); |
||
1479 | |||
1480 | if ( $success ) { |
||
1481 | $this->mTouched = $newTouched; |
||
1482 | $this->clearSharedCache(); |
||
1483 | } else { |
||
1484 | // Clears on failure too since that is desired if the cache is stale |
||
1485 | $this->clearSharedCache( 'refresh' ); |
||
1486 | } |
||
1487 | |||
1488 | return $success; |
||
1489 | } |
||
1490 | |||
1491 | /** |
||
1492 | * Clear various cached data stored in this object. The cache of the user table |
||
1493 | * data (i.e. self::$mCacheVars) is not cleared unless $reloadFrom is given. |
||
1494 | * |
||
1495 | * @param bool|string $reloadFrom Reload user and user_groups table data from a |
||
1496 | * given source. May be "name", "id", "defaults", "session", or false for no reload. |
||
1497 | */ |
||
1498 | public function clearInstanceCache( $reloadFrom = false ) { |
||
1516 | |||
1517 | /** |
||
1518 | * Combine the language default options with any site-specific options |
||
1519 | * and add the default language variants. |
||
1520 | * |
||
1521 | * @return array Array of String options |
||
1522 | */ |
||
1523 | public static function getDefaultOptions() { |
||
1524 | global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin; |
||
1525 | |||
1526 | static $defOpt = null; |
||
1527 | static $defOptLang = null; |
||
1528 | |||
1529 | if ( $defOpt !== null && $defOptLang === $wgContLang->getCode() ) { |
||
1530 | // $wgContLang does not change (and should not change) mid-request, |
||
1531 | // but the unit tests change it anyway, and expect this method to |
||
1532 | // return values relevant to the current $wgContLang. |
||
1533 | return $defOpt; |
||
1534 | } |
||
1535 | |||
1536 | $defOpt = $wgDefaultUserOptions; |
||
1537 | // Default language setting |
||
1538 | $defOptLang = $wgContLang->getCode(); |
||
1539 | $defOpt['language'] = $defOptLang; |
||
1540 | foreach ( LanguageConverter::$languagesWithVariants as $langCode ) { |
||
1541 | $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode; |
||
1542 | } |
||
1543 | |||
1544 | // NOTE: don't use SearchEngineConfig::getSearchableNamespaces here, |
||
1545 | // since extensions may change the set of searchable namespaces depending |
||
1546 | // on user groups/permissions. |
||
1547 | foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) { |
||
1548 | $defOpt['searchNs' . $nsnum] = (boolean)$val; |
||
1549 | } |
||
1550 | $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin ); |
||
1551 | |||
1552 | Hooks::run( 'UserGetDefaultOptions', [ &$defOpt ] ); |
||
1553 | |||
1554 | return $defOpt; |
||
1555 | } |
||
1556 | |||
1557 | /** |
||
1558 | * Get a given default option value. |
||
1559 | * |
||
1560 | * @param string $opt Name of option to retrieve |
||
1561 | * @return string Default option value |
||
1562 | */ |
||
1563 | public static function getDefaultOption( $opt ) { |
||
1564 | $defOpts = self::getDefaultOptions(); |
||
1565 | if ( isset( $defOpts[$opt] ) ) { |
||
1566 | return $defOpts[$opt]; |
||
1567 | } else { |
||
1568 | return null; |
||
1569 | } |
||
1570 | } |
||
1571 | |||
1572 | /** |
||
1573 | * Get blocking information |
||
1574 | * @param bool $bFromSlave Whether to check the replica DB first. |
||
1575 | * To improve performance, non-critical checks are done against replica DBs. |
||
1576 | * Check when actually saving should be done against master. |
||
1577 | */ |
||
1578 | private function getBlockedStatus( $bFromSlave = true ) { |
||
1579 | global $wgProxyWhitelist, $wgUser, $wgApplyIpBlocksToXff; |
||
1580 | |||
1581 | if ( -1 != $this->mBlockedby ) { |
||
1582 | return; |
||
1583 | } |
||
1584 | |||
1585 | wfDebug( __METHOD__ . ": checking...\n" ); |
||
1586 | |||
1587 | // Initialize data... |
||
1588 | // Otherwise something ends up stomping on $this->mBlockedby when |
||
1589 | // things get lazy-loaded later, causing false positive block hits |
||
1590 | // due to -1 !== 0. Probably session-related... Nothing should be |
||
1591 | // overwriting mBlockedby, surely? |
||
1592 | $this->load(); |
||
1593 | |||
1594 | # We only need to worry about passing the IP address to the Block generator if the |
||
1595 | # user is not immune to autoblocks/hardblocks, and they are the current user so we |
||
1596 | # know which IP address they're actually coming from |
||
1597 | $ip = null; |
||
1598 | if ( !$this->isAllowed( 'ipblock-exempt' ) ) { |
||
1599 | // $wgUser->getName() only works after the end of Setup.php. Until |
||
1600 | // then, assume it's a logged-out user. |
||
1601 | $globalUserName = $wgUser->isSafeToLoad() |
||
1602 | ? $wgUser->getName() |
||
1603 | : IP::sanitizeIP( $wgUser->getRequest()->getIP() ); |
||
1604 | if ( $this->getName() === $globalUserName ) { |
||
1605 | $ip = $this->getRequest()->getIP(); |
||
1606 | } |
||
1607 | } |
||
1608 | |||
1609 | // User/IP blocking |
||
1610 | $block = Block::newFromTarget( $this, $ip, !$bFromSlave ); |
||
1611 | |||
1612 | // Proxy blocking |
||
1613 | if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) { |
||
1614 | // Local list |
||
1615 | if ( self::isLocallyBlockedProxy( $ip ) ) { |
||
1616 | $block = new Block; |
||
1617 | $block->setBlocker( wfMessage( 'proxyblocker' )->text() ); |
||
1618 | $block->mReason = wfMessage( 'proxyblockreason' )->text(); |
||
1619 | $block->setTarget( $ip ); |
||
1620 | } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) { |
||
1621 | $block = new Block; |
||
1622 | $block->setBlocker( wfMessage( 'sorbs' )->text() ); |
||
1623 | $block->mReason = wfMessage( 'sorbsreason' )->text(); |
||
1624 | $block->setTarget( $ip ); |
||
1625 | } |
||
1626 | } |
||
1627 | |||
1628 | // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled |
||
1629 | if ( !$block instanceof Block |
||
1630 | && $wgApplyIpBlocksToXff |
||
1631 | && $ip !== null |
||
1632 | && !in_array( $ip, $wgProxyWhitelist ) |
||
1633 | ) { |
||
1634 | $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' ); |
||
1635 | $xff = array_map( 'trim', explode( ',', $xff ) ); |
||
1636 | $xff = array_diff( $xff, [ $ip ] ); |
||
1637 | $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromSlave ); |
||
1638 | $block = Block::chooseBlock( $xffblocks, $xff ); |
||
1639 | if ( $block instanceof Block ) { |
||
1640 | # Mangle the reason to alert the user that the block |
||
1641 | # originated from matching the X-Forwarded-For header. |
||
1642 | $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->text(); |
||
1643 | } |
||
1644 | } |
||
1645 | |||
1646 | if ( $block instanceof Block ) { |
||
1647 | wfDebug( __METHOD__ . ": Found block.\n" ); |
||
1648 | $this->mBlock = $block; |
||
1649 | $this->mBlockedby = $block->getByName(); |
||
1650 | $this->mBlockreason = $block->mReason; |
||
1651 | $this->mHideName = $block->mHideName; |
||
1652 | $this->mAllowUsertalk = !$block->prevents( 'editownusertalk' ); |
||
1653 | } else { |
||
1654 | $this->mBlockedby = ''; |
||
1655 | $this->mHideName = 0; |
||
1656 | $this->mAllowUsertalk = false; |
||
1657 | } |
||
1658 | |||
1659 | // Extensions |
||
1660 | Hooks::run( 'GetBlockedStatus', [ &$this ] ); |
||
1661 | } |
||
1662 | |||
1663 | /** |
||
1664 | * Whether the given IP is in a DNS blacklist. |
||
1665 | * |
||
1666 | * @param string $ip IP to check |
||
1667 | * @param bool $checkWhitelist Whether to check the whitelist first |
||
1668 | * @return bool True if blacklisted. |
||
1669 | */ |
||
1670 | public function isDnsBlacklisted( $ip, $checkWhitelist = false ) { |
||
1683 | |||
1684 | /** |
||
1685 | * Whether the given IP is in a given DNS blacklist. |
||
1686 | * |
||
1687 | * @param string $ip IP to check |
||
1688 | * @param string|array $bases Array of Strings: URL of the DNS blacklist |
||
1689 | * @return bool True if blacklisted. |
||
1690 | */ |
||
1691 | public function inDnsBlacklist( $ip, $bases ) { |
||
1692 | $found = false; |
||
1693 | // @todo FIXME: IPv6 ??? (https://bugs.php.net/bug.php?id=33170) |
||
1694 | if ( IP::isIPv4( $ip ) ) { |
||
1695 | // Reverse IP, bug 21255 |
||
1696 | $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) ); |
||
1697 | |||
1698 | foreach ( (array)$bases as $base ) { |
||
1699 | // Make hostname |
||
1700 | // If we have an access key, use that too (ProjectHoneypot, etc.) |
||
1701 | $basename = $base; |
||
1702 | if ( is_array( $base ) ) { |
||
1703 | if ( count( $base ) >= 2 ) { |
||
1704 | // Access key is 1, base URL is 0 |
||
1705 | $host = "{$base[1]}.$ipReversed.{$base[0]}"; |
||
1706 | } else { |
||
1707 | $host = "$ipReversed.{$base[0]}"; |
||
1708 | } |
||
1709 | $basename = $base[0]; |
||
1710 | } else { |
||
1711 | $host = "$ipReversed.$base"; |
||
1712 | } |
||
1713 | |||
1714 | // Send query |
||
1715 | $ipList = gethostbynamel( $host ); |
||
1716 | |||
1717 | if ( $ipList ) { |
||
1718 | wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $basename!" ); |
||
1719 | $found = true; |
||
1720 | break; |
||
1721 | } else { |
||
1722 | wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." ); |
||
1729 | |||
1730 | /** |
||
1731 | * Check if an IP address is in the local proxy list |
||
1732 | * |
||
1733 | * @param string $ip |
||
1734 | * |
||
1735 | * @return bool |
||
1736 | */ |
||
1737 | public static function isLocallyBlockedProxy( $ip ) { |
||
1761 | |||
1762 | /** |
||
1763 | * Is this user subject to rate limiting? |
||
1764 | * |
||
1765 | * @return bool True if rate limited |
||
1766 | */ |
||
1767 | public function isPingLimitable() { |
||
1777 | |||
1778 | /** |
||
1779 | * Primitive rate limits: enforce maximum actions per time period |
||
1780 | * to put a brake on flooding. |
||
1781 | * |
||
1782 | * The method generates both a generic profiling point and a per action one |
||
1783 | * (suffix being "-$action". |
||
1784 | * |
||
1785 | * @note When using a shared cache like memcached, IP-address |
||
1786 | * last-hit counters will be shared across wikis. |
||
1787 | * |
||
1788 | * @param string $action Action to enforce; 'edit' if unspecified |
||
1789 | * @param int $incrBy Positive amount to increment counter by [defaults to 1] |
||
1790 | * @return bool True if a rate limiter was tripped |
||
1791 | */ |
||
1792 | public function pingLimiter( $action = 'edit', $incrBy = 1 ) { |
||
1924 | |||
1925 | /** |
||
1926 | * Check if user is blocked |
||
1927 | * |
||
1928 | * @param bool $bFromSlave Whether to check the replica DB instead of |
||
1929 | * the master. Hacked from false due to horrible probs on site. |
||
1930 | * @return bool True if blocked, false otherwise |
||
1931 | */ |
||
1932 | public function isBlocked( $bFromSlave = true ) { |
||
1933 | return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' ); |
||
1934 | } |
||
1935 | |||
1936 | /** |
||
1937 | * Get the block affecting the user, or null if the user is not blocked |
||
1938 | * |
||
1939 | * @param bool $bFromSlave Whether to check the replica DB instead of the master |
||
1940 | * @return Block|null |
||
1941 | */ |
||
1942 | public function getBlock( $bFromSlave = true ) { |
||
1946 | |||
1947 | /** |
||
1948 | * Check if user is blocked from editing a particular article |
||
1949 | * |
||
1950 | * @param Title $title Title to check |
||
1951 | * @param bool $bFromSlave Whether to check the replica DB instead of the master |
||
1952 | * @return bool |
||
1953 | */ |
||
1954 | public function isBlockedFrom( $title, $bFromSlave = false ) { |
||
1970 | |||
1971 | /** |
||
1972 | * If user is blocked, return the name of the user who placed the block |
||
1973 | * @return string Name of blocker |
||
1974 | */ |
||
1975 | public function blockedBy() { |
||
1979 | |||
1980 | /** |
||
1981 | * If user is blocked, return the specified reason for the block |
||
1982 | * @return string Blocking reason |
||
1983 | */ |
||
1984 | public function blockedFor() { |
||
1988 | |||
1989 | /** |
||
1990 | * If user is blocked, return the ID for the block |
||
1991 | * @return int Block ID |
||
1992 | */ |
||
1993 | public function getBlockId() { |
||
1997 | |||
1998 | /** |
||
1999 | * Check if user is blocked on all wikis. |
||
2000 | * Do not use for actual edit permission checks! |
||
2001 | * This is intended for quick UI checks. |
||
2002 | * |
||
2003 | * @param string $ip IP address, uses current client if none given |
||
2004 | * @return bool True if blocked, false otherwise |
||
2005 | */ |
||
2006 | public function isBlockedGlobally( $ip = '' ) { |
||
2009 | |||
2010 | /** |
||
2011 | * Check if user is blocked on all wikis. |
||
2012 | * Do not use for actual edit permission checks! |
||
2013 | * This is intended for quick UI checks. |
||
2014 | * |
||
2015 | * @param string $ip IP address, uses current client if none given |
||
2016 | * @return Block|null Block object if blocked, null otherwise |
||
2017 | * @throws FatalError |
||
2018 | * @throws MWException |
||
2019 | */ |
||
2020 | public function getGlobalBlock( $ip = '' ) { |
||
2043 | |||
2044 | /** |
||
2045 | * Check if user account is locked |
||
2046 | * |
||
2047 | * @return bool True if locked, false otherwise |
||
2048 | */ |
||
2049 | View Code Duplication | public function isLocked() { |
|
2058 | |||
2059 | /** |
||
2060 | * Check if user account is hidden |
||
2061 | * |
||
2062 | * @return bool True if hidden, false otherwise |
||
2063 | */ |
||
2064 | View Code Duplication | public function isHidden() { |
|
2076 | |||
2077 | /** |
||
2078 | * Get the user's ID. |
||
2079 | * @return int The user's ID; 0 if the user is anonymous or nonexistent |
||
2080 | */ |
||
2081 | public function getId() { |
||
2092 | |||
2093 | /** |
||
2094 | * Set the user and reload all fields according to a given ID |
||
2095 | * @param int $v User ID to reload |
||
2096 | */ |
||
2097 | public function setId( $v ) { |
||
2101 | |||
2102 | /** |
||
2103 | * Get the user name, or the IP of an anonymous user |
||
2104 | * @return string User's name or IP address |
||
2105 | */ |
||
2106 | public function getName() { |
||
2119 | |||
2120 | /** |
||
2121 | * Set the user name. |
||
2122 | * |
||
2123 | * This does not reload fields from the database according to the given |
||
2124 | * name. Rather, it is used to create a temporary "nonexistent user" for |
||
2125 | * later addition to the database. It can also be used to set the IP |
||
2126 | * address for an anonymous user to something other than the current |
||
2127 | * remote IP. |
||
2128 | * |
||
2129 | * @note User::newFromName() has roughly the same function, when the named user |
||
2130 | * does not exist. |
||
2131 | * @param string $str New user name to set |
||
2132 | */ |
||
2133 | public function setName( $str ) { |
||
2137 | |||
2138 | /** |
||
2139 | * Get the user's name escaped by underscores. |
||
2140 | * @return string Username escaped by underscores. |
||
2141 | */ |
||
2142 | public function getTitleKey() { |
||
2145 | |||
2146 | /** |
||
2147 | * Check if the user has new messages. |
||
2148 | * @return bool True if the user has new messages |
||
2149 | */ |
||
2150 | public function getNewtalk() { |
||
2174 | |||
2175 | /** |
||
2176 | * Return the data needed to construct links for new talk page message |
||
2177 | * alerts. If there are new messages, this will return an associative array |
||
2178 | * with the following data: |
||
2179 | * wiki: The database name of the wiki |
||
2180 | * link: Root-relative link to the user's talk page |
||
2181 | * rev: The last talk page revision that the user has seen or null. This |
||
2182 | * is useful for building diff links. |
||
2183 | * If there are no new messages, it returns an empty array. |
||
2184 | * @note This function was designed to accomodate multiple talk pages, but |
||
2185 | * currently only returns a single link and revision. |
||
2186 | * @return array |
||
2187 | */ |
||
2188 | public function getNewMessageLinks() { |
||
2205 | |||
2206 | /** |
||
2207 | * Get the revision ID for the last talk page revision viewed by the talk |
||
2208 | * page owner. |
||
2209 | * @return int|null Revision ID or null |
||
2210 | */ |
||
2211 | public function getNewMessageRevisionId() { |
||
2229 | |||
2230 | /** |
||
2231 | * Internal uncached check for new messages |
||
2232 | * |
||
2233 | * @see getNewtalk() |
||
2234 | * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise |
||
2235 | * @param string|int $id User's IP address for anonymous users, User ID otherwise |
||
2236 | * @return bool True if the user has new messages |
||
2237 | */ |
||
2238 | protected function checkNewtalk( $field, $id ) { |
||
2245 | |||
2246 | /** |
||
2247 | * Add or update the new messages flag |
||
2248 | * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise |
||
2249 | * @param string|int $id User's IP address for anonymous users, User ID otherwise |
||
2250 | * @param Revision|null $curRev New, as yet unseen revision of the user talk page. Ignored if null. |
||
2251 | * @return bool True if successful, false otherwise |
||
2252 | */ |
||
2253 | protected function updateNewtalk( $field, $id, $curRev = null ) { |
||
2271 | |||
2272 | /** |
||
2273 | * Clear the new messages flag for the given user |
||
2274 | * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise |
||
2275 | * @param string|int $id User's IP address for anonymous users, User ID otherwise |
||
2276 | * @return bool True if successful, false otherwise |
||
2277 | */ |
||
2278 | protected function deleteNewtalk( $field, $id ) { |
||
2291 | |||
2292 | /** |
||
2293 | * Update the 'You have new messages!' status. |
||
2294 | * @param bool $val Whether the user has new messages |
||
2295 | * @param Revision $curRev New, as yet unseen revision of the user talk |
||
2296 | * page. Ignored if null or !$val. |
||
2297 | */ |
||
2298 | public function setNewtalk( $val, $curRev = null ) { |
||
2324 | |||
2325 | /** |
||
2326 | * Generate a current or new-future timestamp to be stored in the |
||
2327 | * user_touched field when we update things. |
||
2328 | * @return string Timestamp in TS_MW format |
||
2329 | */ |
||
2330 | private function newTouchedTimestamp() { |
||
2340 | |||
2341 | /** |
||
2342 | * Clear user data from memcached |
||
2343 | * |
||
2344 | * Use after applying updates to the database; caller's |
||
2345 | * responsibility to update user_touched if appropriate. |
||
2346 | * |
||
2347 | * Called implicitly from invalidateCache() and saveSettings(). |
||
2348 | * |
||
2349 | * @param string $mode Use 'refresh' to clear now; otherwise before DB commit |
||
2350 | */ |
||
2351 | public function clearSharedCache( $mode = 'changed' ) { |
||
2369 | |||
2370 | /** |
||
2371 | * Immediately touch the user data cache for this account |
||
2372 | * |
||
2373 | * Calls touch() and removes account data from memcached |
||
2374 | */ |
||
2375 | public function invalidateCache() { |
||
2379 | |||
2380 | /** |
||
2381 | * Update the "touched" timestamp for the user |
||
2382 | * |
||
2383 | * This is useful on various login/logout events when making sure that |
||
2384 | * a browser or proxy that has multiple tenants does not suffer cache |
||
2385 | * pollution where the new user sees the old users content. The value |
||
2386 | * of getTouched() is checked when determining 304 vs 200 responses. |
||
2387 | * Unlike invalidateCache(), this preserves the User object cache and |
||
2388 | * avoids database writes. |
||
2389 | * |
||
2390 | * @since 1.25 |
||
2391 | */ |
||
2392 | public function touch() { |
||
2400 | |||
2401 | /** |
||
2402 | * Validate the cache for this account. |
||
2403 | * @param string $timestamp A timestamp in TS_MW format |
||
2404 | * @return bool |
||
2405 | */ |
||
2406 | public function validateCache( $timestamp ) { |
||
2409 | |||
2410 | /** |
||
2411 | * Get the user touched timestamp |
||
2412 | * |
||
2413 | * Use this value only to validate caches via inequalities |
||
2414 | * such as in the case of HTTP If-Modified-Since response logic |
||
2415 | * |
||
2416 | * @return string TS_MW Timestamp |
||
2417 | */ |
||
2418 | public function getTouched() { |
||
2434 | |||
2435 | /** |
||
2436 | * Get the user_touched timestamp field (time of last DB updates) |
||
2437 | * @return string TS_MW Timestamp |
||
2438 | * @since 1.26 |
||
2439 | */ |
||
2440 | public function getDBTouched() { |
||
2445 | |||
2446 | /** |
||
2447 | * @deprecated Removed in 1.27. |
||
2448 | * @return Password |
||
2449 | * @since 1.24 |
||
2450 | */ |
||
2451 | public function getPassword() { |
||
2454 | |||
2455 | /** |
||
2456 | * @deprecated Removed in 1.27. |
||
2457 | * @return Password |
||
2458 | * @since 1.24 |
||
2459 | */ |
||
2460 | public function getTemporaryPassword() { |
||
2463 | |||
2464 | /** |
||
2465 | * Set the password and reset the random token. |
||
2466 | * Calls through to authentication plugin if necessary; |
||
2467 | * will have no effect if the auth plugin refuses to |
||
2468 | * pass the change through or if the legal password |
||
2469 | * checks fail. |
||
2470 | * |
||
2471 | * As a special case, setting the password to null |
||
2472 | * wipes it, so the account cannot be logged in until |
||
2473 | * a new password is set, for instance via e-mail. |
||
2474 | * |
||
2475 | * @deprecated since 1.27, use AuthManager instead |
||
2476 | * @param string $str New password to set |
||
2477 | * @throws PasswordError On failure |
||
2478 | * @return bool |
||
2479 | */ |
||
2480 | public function setPassword( $str ) { |
||
2483 | |||
2484 | /** |
||
2485 | * Set the password and reset the random token unconditionally. |
||
2486 | * |
||
2487 | * @deprecated since 1.27, use AuthManager instead |
||
2488 | * @param string|null $str New password to set or null to set an invalid |
||
2489 | * password hash meaning that the user will not be able to log in |
||
2490 | * through the web interface. |
||
2491 | */ |
||
2492 | public function setInternalPassword( $str ) { |
||
2495 | |||
2496 | /** |
||
2497 | * Actually set the password and such |
||
2498 | * @since 1.27 cannot set a password for a user not in the database |
||
2499 | * @param string|null $str New password to set or null to set an invalid |
||
2500 | * password hash meaning that the user will not be able to log in |
||
2501 | * through the web interface. |
||
2502 | * @return bool Success |
||
2503 | */ |
||
2504 | private function setPasswordInternal( $str ) { |
||
2529 | |||
2530 | /** |
||
2531 | * Changes credentials of the user. |
||
2532 | * |
||
2533 | * This is a convenience wrapper around AuthManager::changeAuthenticationData. |
||
2534 | * Note that this can return a status that isOK() but not isGood() on certain types of failures, |
||
2535 | * e.g. when no provider handled the change. |
||
2536 | * |
||
2537 | * @param array $data A set of authentication data in fieldname => value format. This is the |
||
2538 | * same data you would pass the changeauthenticationdata API - 'username', 'password' etc. |
||
2539 | * @return Status |
||
2540 | * @since 1.27 |
||
2541 | */ |
||
2542 | public function changeAuthenticationData( array $data ) { |
||
2562 | |||
2563 | /** |
||
2564 | * Get the user's current token. |
||
2565 | * @param bool $forceCreation Force the generation of a new token if the |
||
2566 | * user doesn't have one (default=true for backwards compatibility). |
||
2567 | * @return string|null Token |
||
2568 | */ |
||
2569 | public function getToken( $forceCreation = true ) { |
||
2600 | |||
2601 | /** |
||
2602 | * Set the random token (used for persistent authentication) |
||
2603 | * Called from loadDefaults() among other places. |
||
2604 | * |
||
2605 | * @param string|bool $token If specified, set the token to this value |
||
2606 | */ |
||
2607 | public function setToken( $token = false ) { |
||
2618 | |||
2619 | /** |
||
2620 | * Set the password for a password reminder or new account email |
||
2621 | * |
||
2622 | * @deprecated Removed in 1.27. Use PasswordReset instead. |
||
2623 | * @param string $str New password to set or null to set an invalid |
||
2624 | * password hash meaning that the user will not be able to use it |
||
2625 | * @param bool $throttle If true, reset the throttle timestamp to the present |
||
2626 | */ |
||
2627 | public function setNewpassword( $str, $throttle = true ) { |
||
2630 | |||
2631 | /** |
||
2632 | * Has password reminder email been sent within the last |
||
2633 | * $wgPasswordReminderResendTime hours? |
||
2634 | * @deprecated Removed in 1.27. See above. |
||
2635 | * @return bool |
||
2636 | */ |
||
2637 | public function isPasswordReminderThrottled() { |
||
2640 | |||
2641 | /** |
||
2642 | * Get the user's e-mail address |
||
2643 | * @return string User's email address |
||
2644 | */ |
||
2645 | public function getEmail() { |
||
2650 | |||
2651 | /** |
||
2652 | * Get the timestamp of the user's e-mail authentication |
||
2653 | * @return string TS_MW timestamp |
||
2654 | */ |
||
2655 | public function getEmailAuthenticationTimestamp() { |
||
2660 | |||
2661 | /** |
||
2662 | * Set the user's e-mail address |
||
2663 | * @param string $str New e-mail address |
||
2664 | */ |
||
2665 | public function setEmail( $str ) { |
||
2674 | |||
2675 | /** |
||
2676 | * Set the user's e-mail address and a confirmation mail if needed. |
||
2677 | * |
||
2678 | * @since 1.20 |
||
2679 | * @param string $str New e-mail address |
||
2680 | * @return Status |
||
2681 | */ |
||
2682 | public function setEmailWithConfirmation( $str ) { |
||
2732 | |||
2733 | /** |
||
2734 | * Get the user's real name |
||
2735 | * @return string User's real name |
||
2736 | */ |
||
2737 | public function getRealName() { |
||
2744 | |||
2745 | /** |
||
2746 | * Set the user's real name |
||
2747 | * @param string $str New real name |
||
2748 | */ |
||
2749 | public function setRealName( $str ) { |
||
2753 | |||
2754 | /** |
||
2755 | * Get the user's current setting for a given option. |
||
2756 | * |
||
2757 | * @param string $oname The option to check |
||
2758 | * @param string $defaultOverride A default value returned if the option does not exist |
||
2759 | * @param bool $ignoreHidden Whether to ignore the effects of $wgHiddenPrefs |
||
2760 | * @return string User's current value for the option |
||
2761 | * @see getBoolOption() |
||
2762 | * @see getIntOption() |
||
2763 | */ |
||
2764 | public function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) { |
||
2783 | |||
2784 | /** |
||
2785 | * Get all user's options |
||
2786 | * |
||
2787 | * @param int $flags Bitwise combination of: |
||
2788 | * User::GETOPTIONS_EXCLUDE_DEFAULTS Exclude user options that are set |
||
2789 | * to the default value. (Since 1.25) |
||
2790 | * @return array |
||
2791 | */ |
||
2792 | public function getOptions( $flags = 0 ) { |
||
2815 | |||
2816 | /** |
||
2817 | * Get the user's current setting for a given option, as a boolean value. |
||
2818 | * |
||
2819 | * @param string $oname The option to check |
||
2820 | * @return bool User's current value for the option |
||
2821 | * @see getOption() |
||
2822 | */ |
||
2823 | public function getBoolOption( $oname ) { |
||
2826 | |||
2827 | /** |
||
2828 | * Get the user's current setting for a given option, as an integer value. |
||
2829 | * |
||
2830 | * @param string $oname The option to check |
||
2831 | * @param int $defaultOverride A default value returned if the option does not exist |
||
2832 | * @return int User's current value for the option |
||
2833 | * @see getOption() |
||
2834 | */ |
||
2835 | public function getIntOption( $oname, $defaultOverride = 0 ) { |
||
2842 | |||
2843 | /** |
||
2844 | * Set the given option for a user. |
||
2845 | * |
||
2846 | * You need to call saveSettings() to actually write to the database. |
||
2847 | * |
||
2848 | * @param string $oname The option to set |
||
2849 | * @param mixed $val New value to set |
||
2850 | */ |
||
2851 | public function setOption( $oname, $val ) { |
||
2861 | |||
2862 | /** |
||
2863 | * Get a token stored in the preferences (like the watchlist one), |
||
2864 | * resetting it if it's empty (and saving changes). |
||
2865 | * |
||
2866 | * @param string $oname The option name to retrieve the token from |
||
2867 | * @return string|bool User's current value for the option, or false if this option is disabled. |
||
2868 | * @see resetTokenFromOption() |
||
2869 | * @see getOption() |
||
2870 | * @deprecated since 1.26 Applications should use the OAuth extension |
||
2871 | */ |
||
2872 | public function getTokenFromOption( $oname ) { |
||
2890 | |||
2891 | /** |
||
2892 | * Reset a token stored in the preferences (like the watchlist one). |
||
2893 | * *Does not* save user's preferences (similarly to setOption()). |
||
2894 | * |
||
2895 | * @param string $oname The option name to reset the token in |
||
2896 | * @return string|bool New token value, or false if this option is disabled. |
||
2897 | * @see getTokenFromOption() |
||
2898 | * @see setOption() |
||
2899 | */ |
||
2900 | public function resetTokenFromOption( $oname ) { |
||
2910 | |||
2911 | /** |
||
2912 | * Return a list of the types of user options currently returned by |
||
2913 | * User::getOptionKinds(). |
||
2914 | * |
||
2915 | * Currently, the option kinds are: |
||
2916 | * - 'registered' - preferences which are registered in core MediaWiki or |
||
2917 | * by extensions using the UserGetDefaultOptions hook. |
||
2918 | * - 'registered-multiselect' - as above, using the 'multiselect' type. |
||
2919 | * - 'registered-checkmatrix' - as above, using the 'checkmatrix' type. |
||
2920 | * - 'userjs' - preferences with names starting with 'userjs-', intended to |
||
2921 | * be used by user scripts. |
||
2922 | * - 'special' - "preferences" that are not accessible via User::getOptions |
||
2923 | * or User::setOptions. |
||
2924 | * - 'unused' - preferences about which MediaWiki doesn't know anything. |
||
2925 | * These are usually legacy options, removed in newer versions. |
||
2926 | * |
||
2927 | * The API (and possibly others) use this function to determine the possible |
||
2928 | * option types for validation purposes, so make sure to update this when a |
||
2929 | * new option kind is added. |
||
2930 | * |
||
2931 | * @see User::getOptionKinds |
||
2932 | * @return array Option kinds |
||
2933 | */ |
||
2934 | public static function listOptionKinds() { |
||
2944 | |||
2945 | /** |
||
2946 | * Return an associative array mapping preferences keys to the kind of a preference they're |
||
2947 | * used for. Different kinds are handled differently when setting or reading preferences. |
||
2948 | * |
||
2949 | * See User::listOptionKinds for the list of valid option types that can be provided. |
||
2950 | * |
||
2951 | * @see User::listOptionKinds |
||
2952 | * @param IContextSource $context |
||
2953 | * @param array $options Assoc. array with options keys to check as keys. |
||
2954 | * Defaults to $this->mOptions. |
||
2955 | * @return array The key => kind mapping data |
||
2956 | */ |
||
2957 | public function getOptionKinds( IContextSource $context, $options = null ) { |
||
3026 | |||
3027 | /** |
||
3028 | * Reset certain (or all) options to the site defaults |
||
3029 | * |
||
3030 | * The optional parameter determines which kinds of preferences will be reset. |
||
3031 | * Supported values are everything that can be reported by getOptionKinds() |
||
3032 | * and 'all', which forces a reset of *all* preferences and overrides everything else. |
||
3033 | * |
||
3034 | * @param array|string $resetKinds Which kinds of preferences to reset. Defaults to |
||
3035 | * array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ) |
||
3036 | * for backwards-compatibility. |
||
3037 | * @param IContextSource|null $context Context source used when $resetKinds |
||
3038 | * does not contain 'all', passed to getOptionKinds(). |
||
3039 | * Defaults to RequestContext::getMain() when null. |
||
3040 | */ |
||
3041 | public function resetOptions( |
||
3081 | |||
3082 | /** |
||
3083 | * Get the user's preferred date format. |
||
3084 | * @return string User's preferred date format |
||
3085 | */ |
||
3086 | public function getDatePreference() { |
||
3099 | |||
3100 | /** |
||
3101 | * Determine based on the wiki configuration and the user's options, |
||
3102 | * whether this user must be over HTTPS no matter what. |
||
3103 | * |
||
3104 | * @return bool |
||
3105 | */ |
||
3106 | public function requiresHTTPS() { |
||
3119 | |||
3120 | /** |
||
3121 | * Get the user preferred stub threshold |
||
3122 | * |
||
3123 | * @return int |
||
3124 | */ |
||
3125 | public function getStubThreshold() { |
||
3135 | |||
3136 | /** |
||
3137 | * Get the permissions this user has. |
||
3138 | * @return array Array of String permission names |
||
3139 | */ |
||
3140 | public function getRights() { |
||
3175 | |||
3176 | /** |
||
3177 | * Get the list of explicit group memberships this user has. |
||
3178 | * The implicit * and user groups are not included. |
||
3179 | * @return array Array of String internal group names |
||
3180 | */ |
||
3181 | public function getGroups() { |
||
3186 | |||
3187 | /** |
||
3188 | * Get the list of implicit group memberships this user has. |
||
3189 | * This includes all explicit groups, plus 'user' if logged in, |
||
3190 | * '*' for all accounts, and autopromoted groups |
||
3191 | * @param bool $recache Whether to avoid the cache |
||
3192 | * @return array Array of String internal group names |
||
3193 | */ |
||
3194 | public function getEffectiveGroups( $recache = false ) { |
||
3207 | |||
3208 | /** |
||
3209 | * Get the list of implicit group memberships this user has. |
||
3210 | * This includes 'user' if logged in, '*' for all accounts, |
||
3211 | * and autopromoted groups |
||
3212 | * @param bool $recache Whether to avoid the cache |
||
3213 | * @return array Array of String internal group names |
||
3214 | */ |
||
3215 | public function getAutomaticGroups( $recache = false ) { |
||
3234 | |||
3235 | /** |
||
3236 | * Returns the groups the user has belonged to. |
||
3237 | * |
||
3238 | * The user may still belong to the returned groups. Compare with getGroups(). |
||
3239 | * |
||
3240 | * The function will not return groups the user had belonged to before MW 1.17 |
||
3241 | * |
||
3242 | * @return array Names of the groups the user has belonged to. |
||
3243 | */ |
||
3244 | View Code Duplication | public function getFormerGroups() { |
|
3263 | |||
3264 | /** |
||
3265 | * Get the user's edit count. |
||
3266 | * @return int|null Null for anonymous users |
||
3267 | */ |
||
3268 | public function getEditCount() { |
||
3291 | |||
3292 | /** |
||
3293 | * Add the user to the given group. |
||
3294 | * This takes immediate effect. |
||
3295 | * @param string $group Name of the group to add |
||
3296 | * @return bool |
||
3297 | */ |
||
3298 | public function addGroup( $group ) { |
||
3331 | |||
3332 | /** |
||
3333 | * Remove the user from the given group. |
||
3334 | * This takes immediate effect. |
||
3335 | * @param string $group Name of the group to remove |
||
3336 | * @return bool |
||
3337 | */ |
||
3338 | public function removeGroup( $group ) { |
||
3373 | |||
3374 | /** |
||
3375 | * Get whether the user is logged in |
||
3376 | * @return bool |
||
3377 | */ |
||
3378 | public function isLoggedIn() { |
||
3381 | |||
3382 | /** |
||
3383 | * Get whether the user is anonymous |
||
3384 | * @return bool |
||
3385 | */ |
||
3386 | public function isAnon() { |
||
3389 | |||
3390 | /** |
||
3391 | * @return bool Whether this user is flagged as being a bot role account |
||
3392 | * @since 1.28 |
||
3393 | */ |
||
3394 | public function isBot() { |
||
3404 | |||
3405 | /** |
||
3406 | * Check if user is allowed to access a feature / make an action |
||
3407 | * |
||
3408 | * @param string ... Permissions to test |
||
3409 | * @return bool True if user is allowed to perform *any* of the given actions |
||
3410 | */ |
||
3411 | View Code Duplication | public function isAllowedAny() { |
|
3420 | |||
3421 | /** |
||
3422 | * |
||
3423 | * @param string ... Permissions to test |
||
3424 | * @return bool True if the user is allowed to perform *all* of the given actions |
||
3425 | */ |
||
3426 | View Code Duplication | public function isAllowedAll() { |
|
3435 | |||
3436 | /** |
||
3437 | * Internal mechanics of testing a permission |
||
3438 | * @param string $action |
||
3439 | * @return bool |
||
3440 | */ |
||
3441 | public function isAllowed( $action = '' ) { |
||
3449 | |||
3450 | /** |
||
3451 | * Check whether to enable recent changes patrol features for this user |
||
3452 | * @return bool True or false |
||
3453 | */ |
||
3454 | public function useRCPatrol() { |
||
3458 | |||
3459 | /** |
||
3460 | * Check whether to enable new pages patrol features for this user |
||
3461 | * @return bool True or false |
||
3462 | */ |
||
3463 | public function useNPPatrol() { |
||
3470 | |||
3471 | /** |
||
3472 | * Check whether to enable new files patrol features for this user |
||
3473 | * @return bool True or false |
||
3474 | */ |
||
3475 | public function useFilePatrol() { |
||
3482 | |||
3483 | /** |
||
3484 | * Get the WebRequest object to use with this object |
||
3485 | * |
||
3486 | * @return WebRequest |
||
3487 | */ |
||
3488 | public function getRequest() { |
||
3496 | |||
3497 | /** |
||
3498 | * Check the watched status of an article. |
||
3499 | * @since 1.22 $checkRights parameter added |
||
3500 | * @param Title $title Title of the article to look at |
||
3501 | * @param bool $checkRights Whether to check 'viewmywatchlist'/'editmywatchlist' rights. |
||
3502 | * Pass User::CHECK_USER_RIGHTS or User::IGNORE_USER_RIGHTS. |
||
3503 | * @return bool |
||
3504 | */ |
||
3505 | public function isWatched( $title, $checkRights = self::CHECK_USER_RIGHTS ) { |
||
3511 | |||
3512 | /** |
||
3513 | * Watch an article. |
||
3514 | * @since 1.22 $checkRights parameter added |
||
3515 | * @param Title $title Title of the article to look at |
||
3516 | * @param bool $checkRights Whether to check 'viewmywatchlist'/'editmywatchlist' rights. |
||
3517 | * Pass User::CHECK_USER_RIGHTS or User::IGNORE_USER_RIGHTS. |
||
3518 | */ |
||
3519 | public function addWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) { |
||
3528 | |||
3529 | /** |
||
3530 | * Stop watching an article. |
||
3531 | * @since 1.22 $checkRights parameter added |
||
3532 | * @param Title $title Title of the article to look at |
||
3533 | * @param bool $checkRights Whether to check 'viewmywatchlist'/'editmywatchlist' rights. |
||
3534 | * Pass User::CHECK_USER_RIGHTS or User::IGNORE_USER_RIGHTS. |
||
3535 | */ |
||
3536 | public function removeWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) { |
||
3544 | |||
3545 | /** |
||
3546 | * Clear the user's notification timestamp for the given title. |
||
3547 | * If e-notif e-mails are on, they will receive notification mails on |
||
3548 | * the next change of the page if it's watched etc. |
||
3549 | * @note If the user doesn't have 'editmywatchlist', this will do nothing. |
||
3550 | * @param Title $title Title of the article to look at |
||
3551 | * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed. |
||
3552 | */ |
||
3553 | public function clearNotification( &$title, $oldid = 0 ) { |
||
3612 | |||
3613 | /** |
||
3614 | * Resets all of the given user's page-change notification timestamps. |
||
3615 | * If e-notif e-mails are on, they will receive notification mails on |
||
3616 | * the next change of any watched page. |
||
3617 | * @note If the user doesn't have 'editmywatchlist', this will do nothing. |
||
3618 | */ |
||
3619 | public function clearAllNotifications() { |
||
3684 | |||
3685 | /** |
||
3686 | * Set a cookie on the user's client. Wrapper for |
||
3687 | * WebResponse::setCookie |
||
3688 | * @deprecated since 1.27 |
||
3689 | * @param string $name Name of the cookie to set |
||
3690 | * @param string $value Value to set |
||
3691 | * @param int $exp Expiration time, as a UNIX time value; |
||
3692 | * if 0 or not specified, use the default $wgCookieExpiration |
||
3693 | * @param bool $secure |
||
3694 | * true: Force setting the secure attribute when setting the cookie |
||
3695 | * false: Force NOT setting the secure attribute when setting the cookie |
||
3696 | * null (default): Use the default ($wgCookieSecure) to set the secure attribute |
||
3697 | * @param array $params Array of options sent passed to WebResponse::setcookie() |
||
3698 | * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null |
||
3699 | * is passed. |
||
3700 | */ |
||
3701 | protected function setCookie( |
||
3711 | |||
3712 | /** |
||
3713 | * Clear a cookie on the user's client |
||
3714 | * @deprecated since 1.27 |
||
3715 | * @param string $name Name of the cookie to clear |
||
3716 | * @param bool $secure |
||
3717 | * true: Force setting the secure attribute when setting the cookie |
||
3718 | * false: Force NOT setting the secure attribute when setting the cookie |
||
3719 | * null (default): Use the default ($wgCookieSecure) to set the secure attribute |
||
3720 | * @param array $params Array of options sent passed to WebResponse::setcookie() |
||
3721 | */ |
||
3722 | protected function clearCookie( $name, $secure = null, $params = [] ) { |
||
3726 | |||
3727 | /** |
||
3728 | * Set an extended login cookie on the user's client. The expiry of the cookie |
||
3729 | * is controlled by the $wgExtendedLoginCookieExpiration configuration |
||
3730 | * variable. |
||
3731 | * |
||
3732 | * @see User::setCookie |
||
3733 | * |
||
3734 | * @deprecated since 1.27 |
||
3735 | * @param string $name Name of the cookie to set |
||
3736 | * @param string $value Value to set |
||
3737 | * @param bool $secure |
||
3738 | * true: Force setting the secure attribute when setting the cookie |
||
3739 | * false: Force NOT setting the secure attribute when setting the cookie |
||
3740 | * null (default): Use the default ($wgCookieSecure) to set the secure attribute |
||
3741 | */ |
||
3742 | protected function setExtendedLoginCookie( $name, $value, $secure ) { |
||
3754 | |||
3755 | /** |
||
3756 | * Persist this user's session (e.g. set cookies) |
||
3757 | * |
||
3758 | * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null |
||
3759 | * is passed. |
||
3760 | * @param bool $secure Whether to force secure/insecure cookies or use default |
||
3761 | * @param bool $rememberMe Whether to add a Token cookie for elongated sessions |
||
3762 | */ |
||
3763 | public function setCookies( $request = null, $secure = null, $rememberMe = false ) { |
||
3795 | |||
3796 | /** |
||
3797 | * Log this user out. |
||
3798 | */ |
||
3799 | public function logout() { |
||
3800 | if ( Hooks::run( 'UserLogout', [ &$this ] ) ) { |
||
3801 | $this->doLogout(); |
||
3802 | } |
||
3803 | } |
||
3804 | |||
3805 | /** |
||
3806 | * Clear the user's session, and reset the instance cache. |
||
3807 | * @see logout() |
||
3808 | */ |
||
3809 | public function doLogout() { |
||
3840 | |||
3841 | /** |
||
3842 | * Save this user's settings into the database. |
||
3843 | * @todo Only rarely do all these fields need to be set! |
||
3844 | */ |
||
3845 | public function saveSettings() { |
||
3900 | |||
3901 | /** |
||
3902 | * If only this user's username is known, and it exists, return the user ID. |
||
3903 | * |
||
3904 | * @param int $flags Bitfield of User:READ_* constants; useful for existence checks |
||
3905 | * @return int |
||
3906 | */ |
||
3907 | public function idForName( $flags = 0 ) { |
||
3926 | |||
3927 | /** |
||
3928 | * Add a user to the database, return the user object |
||
3929 | * |
||
3930 | * @param string $name Username to add |
||
3931 | * @param array $params Array of Strings Non-default parameters to save to |
||
3932 | * the database as user_* fields: |
||
3933 | * - email: The user's email address. |
||
3934 | * - email_authenticated: The email authentication timestamp. |
||
3935 | * - real_name: The user's real name. |
||
3936 | * - options: An associative array of non-default options. |
||
3937 | * - token: Random authentication token. Do not set. |
||
3938 | * - registration: Registration timestamp. Do not set. |
||
3939 | * |
||
3940 | * @return User|null User object, or null if the username already exists. |
||
3941 | */ |
||
3942 | public static function createNew( $name, $params = [] ) { |
||
3986 | |||
3987 | /** |
||
3988 | * Add this existing user object to the database. If the user already |
||
3989 | * exists, a fatal status object is returned, and the user object is |
||
3990 | * initialised with the data from the database. |
||
3991 | * |
||
3992 | * Previously, this function generated a DB error due to a key conflict |
||
3993 | * if the user already existed. Many extension callers use this function |
||
3994 | * in code along the lines of: |
||
3995 | * |
||
3996 | * $user = User::newFromName( $name ); |
||
3997 | * if ( !$user->isLoggedIn() ) { |
||
3998 | * $user->addToDatabase(); |
||
3999 | * } |
||
4000 | * // do something with $user... |
||
4001 | * |
||
4002 | * However, this was vulnerable to a race condition (bug 16020). By |
||
4003 | * initialising the user object if the user exists, we aim to support this |
||
4004 | * calling sequence as far as possible. |
||
4005 | * |
||
4006 | * Note that if the user exists, this function will acquire a write lock, |
||
4007 | * so it is still advisable to make the call conditional on isLoggedIn(), |
||
4008 | * and to commit the transaction after calling. |
||
4009 | * |
||
4010 | * @throws MWException |
||
4011 | * @return Status |
||
4012 | */ |
||
4013 | public function addToDatabase() { |
||
4071 | |||
4072 | /** |
||
4073 | * If this user is logged-in and blocked, |
||
4074 | * block any IP address they've successfully logged in from. |
||
4075 | * @return bool A block was spread |
||
4076 | */ |
||
4077 | public function spreadAnyEditBlock() { |
||
4084 | |||
4085 | /** |
||
4086 | * If this (non-anonymous) user is blocked, |
||
4087 | * block the IP address they've successfully logged in from. |
||
4088 | * @return bool A block was spread |
||
4089 | */ |
||
4090 | protected function spreadBlock() { |
||
4104 | |||
4105 | /** |
||
4106 | * Get whether the user is explicitly blocked from account creation. |
||
4107 | * @return bool|Block |
||
4108 | */ |
||
4109 | public function isBlockedFromCreateAccount() { |
||
4126 | |||
4127 | /** |
||
4128 | * Get whether the user is blocked from using Special:Emailuser. |
||
4129 | * @return bool |
||
4130 | */ |
||
4131 | public function isBlockedFromEmailuser() { |
||
4135 | |||
4136 | /** |
||
4137 | * Get whether the user is allowed to create an account. |
||
4138 | * @return bool |
||
4139 | */ |
||
4140 | public function isAllowedToCreateAccount() { |
||
4143 | |||
4144 | /** |
||
4145 | * Get this user's personal page title. |
||
4146 | * |
||
4147 | * @return Title User's personal page title |
||
4148 | */ |
||
4149 | public function getUserPage() { |
||
4152 | |||
4153 | /** |
||
4154 | * Get this user's talk page title. |
||
4155 | * |
||
4156 | * @return Title User's talk page title |
||
4157 | */ |
||
4158 | public function getTalkPage() { |
||
4162 | |||
4163 | /** |
||
4164 | * Determine whether the user is a newbie. Newbies are either |
||
4165 | * anonymous IPs, or the most recently created accounts. |
||
4166 | * @return bool |
||
4167 | */ |
||
4168 | public function isNewbie() { |
||
4171 | |||
4172 | /** |
||
4173 | * Check to see if the given clear-text password is one of the accepted passwords |
||
4174 | * @deprecated since 1.27, use AuthManager instead |
||
4175 | * @param string $password User password |
||
4176 | * @return bool True if the given password is correct, otherwise False |
||
4177 | */ |
||
4178 | public function checkPassword( $password ) { |
||
4202 | |||
4203 | /** |
||
4204 | * Check if the given clear-text password matches the temporary password |
||
4205 | * sent by e-mail for password reset operations. |
||
4206 | * |
||
4207 | * @deprecated since 1.27, use AuthManager instead |
||
4208 | * @param string $plaintext |
||
4209 | * @return bool True if matches, false otherwise |
||
4210 | */ |
||
4211 | public function checkTemporaryPassword( $plaintext ) { |
||
4215 | |||
4216 | /** |
||
4217 | * Initialize (if necessary) and return a session token value |
||
4218 | * which can be used in edit forms to show that the user's |
||
4219 | * login credentials aren't being hijacked with a foreign form |
||
4220 | * submission. |
||
4221 | * |
||
4222 | * @since 1.27 |
||
4223 | * @param string|array $salt Array of Strings Optional function-specific data for hashing |
||
4224 | * @param WebRequest|null $request WebRequest object to use or null to use $wgRequest |
||
4225 | * @return MediaWiki\Session\Token The new edit token |
||
4226 | */ |
||
4227 | public function getEditTokenObject( $salt = '', $request = null ) { |
||
4237 | |||
4238 | /** |
||
4239 | * Initialize (if necessary) and return a session token value |
||
4240 | * which can be used in edit forms to show that the user's |
||
4241 | * login credentials aren't being hijacked with a foreign form |
||
4242 | * submission. |
||
4243 | * |
||
4244 | * The $salt for 'edit' and 'csrf' tokens is the default (empty string). |
||
4245 | * |
||
4246 | * @since 1.19 |
||
4247 | * @param string|array $salt Array of Strings Optional function-specific data for hashing |
||
4248 | * @param WebRequest|null $request WebRequest object to use or null to use $wgRequest |
||
4249 | * @return string The new edit token |
||
4250 | */ |
||
4251 | public function getEditToken( $salt = '', $request = null ) { |
||
4254 | |||
4255 | /** |
||
4256 | * Get the embedded timestamp from a token. |
||
4257 | * @deprecated since 1.27, use \MediaWiki\Session\Token::getTimestamp instead. |
||
4258 | * @param string $val Input token |
||
4259 | * @return int|null |
||
4260 | */ |
||
4261 | public static function getEditTokenTimestamp( $val ) { |
||
4265 | |||
4266 | /** |
||
4267 | * Check given value against the token value stored in the session. |
||
4268 | * A match should confirm that the form was submitted from the |
||
4269 | * user's own login session, not a form submission from a third-party |
||
4270 | * site. |
||
4271 | * |
||
4272 | * @param string $val Input value to compare |
||
4273 | * @param string $salt Optional function-specific data for hashing |
||
4274 | * @param WebRequest|null $request Object to use or null to use $wgRequest |
||
4275 | * @param int $maxage Fail tokens older than this, in seconds |
||
4276 | * @return bool Whether the token matches |
||
4277 | */ |
||
4278 | public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) { |
||
4281 | |||
4282 | /** |
||
4283 | * Check given value against the token value stored in the session, |
||
4284 | * ignoring the suffix. |
||
4285 | * |
||
4286 | * @param string $val Input value to compare |
||
4287 | * @param string $salt Optional function-specific data for hashing |
||
4288 | * @param WebRequest|null $request Object to use or null to use $wgRequest |
||
4289 | * @param int $maxage Fail tokens older than this, in seconds |
||
4290 | * @return bool Whether the token matches |
||
4291 | */ |
||
4292 | public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) { |
||
4296 | |||
4297 | /** |
||
4298 | * Generate a new e-mail confirmation token and send a confirmation/invalidation |
||
4299 | * mail to the user's given address. |
||
4300 | * |
||
4301 | * @param string $type Message to send, either "created", "changed" or "set" |
||
4302 | * @return Status |
||
4303 | */ |
||
4304 | public function sendConfirmationMail( $type = 'created' ) { |
||
4331 | |||
4332 | /** |
||
4333 | * Send an e-mail to this user's account. Does not check for |
||
4334 | * confirmed status or validity. |
||
4335 | * |
||
4336 | * @param string $subject Message subject |
||
4337 | * @param string $body Message body |
||
4338 | * @param User|null $from Optional sending user; if unspecified, default |
||
4339 | * $wgPasswordSender will be used. |
||
4340 | * @param string $replyto Reply-To address |
||
4341 | * @return Status |
||
4342 | */ |
||
4343 | public function sendMail( $subject, $body, $from = null, $replyto = null ) { |
||
4358 | |||
4359 | /** |
||
4360 | * Generate, store, and return a new e-mail confirmation code. |
||
4361 | * A hash (unsalted, since it's used as a key) is stored. |
||
4362 | * |
||
4363 | * @note Call saveSettings() after calling this function to commit |
||
4364 | * this change to the database. |
||
4365 | * |
||
4366 | * @param string &$expiration Accepts the expiration time |
||
4367 | * @return string New token |
||
4368 | */ |
||
4369 | protected function confirmationToken( &$expiration ) { |
||
4381 | |||
4382 | /** |
||
4383 | * Return a URL the user can use to confirm their email address. |
||
4384 | * @param string $token Accepts the email confirmation token |
||
4385 | * @return string New token URL |
||
4386 | */ |
||
4387 | protected function confirmationTokenUrl( $token ) { |
||
4390 | |||
4391 | /** |
||
4392 | * Return a URL the user can use to invalidate their email address. |
||
4393 | * @param string $token Accepts the email confirmation token |
||
4394 | * @return string New token URL |
||
4395 | */ |
||
4396 | protected function invalidationTokenUrl( $token ) { |
||
4399 | |||
4400 | /** |
||
4401 | * Internal function to format the e-mail validation/invalidation URLs. |
||
4402 | * This uses a quickie hack to use the |
||
4403 | * hardcoded English names of the Special: pages, for ASCII safety. |
||
4404 | * |
||
4405 | * @note Since these URLs get dropped directly into emails, using the |
||
4406 | * short English names avoids insanely long URL-encoded links, which |
||
4407 | * also sometimes can get corrupted in some browsers/mailers |
||
4408 | * (bug 6957 with Gmail and Internet Explorer). |
||
4409 | * |
||
4410 | * @param string $page Special page |
||
4411 | * @param string $token Token |
||
4412 | * @return string Formatted URL |
||
4413 | */ |
||
4414 | protected function getTokenUrl( $page, $token ) { |
||
4419 | |||
4420 | /** |
||
4421 | * Mark the e-mail address confirmed. |
||
4422 | * |
||
4423 | * @note Call saveSettings() after calling this function to commit the change. |
||
4424 | * |
||
4425 | * @return bool |
||
4426 | */ |
||
4427 | public function confirmEmail() { |
||
4436 | |||
4437 | /** |
||
4438 | * Invalidate the user's e-mail confirmation, and unauthenticate the e-mail |
||
4439 | * address if it was already confirmed. |
||
4440 | * |
||
4441 | * @note Call saveSettings() after calling this function to commit the change. |
||
4442 | * @return bool Returns true |
||
4443 | */ |
||
4444 | public function invalidateEmail() { |
||
4453 | |||
4454 | /** |
||
4455 | * Set the e-mail authentication timestamp. |
||
4456 | * @param string $timestamp TS_MW timestamp |
||
4457 | */ |
||
4458 | public function setEmailAuthenticationTimestamp( $timestamp ) { |
||
4463 | |||
4464 | /** |
||
4465 | * Is this user allowed to send e-mails within limits of current |
||
4466 | * site configuration? |
||
4467 | * @return bool |
||
4468 | */ |
||
4469 | public function canSendEmail() { |
||
4478 | |||
4479 | /** |
||
4480 | * Is this user allowed to receive e-mails within limits of current |
||
4481 | * site configuration? |
||
4482 | * @return bool |
||
4483 | */ |
||
4484 | public function canReceiveEmail() { |
||
4487 | |||
4488 | /** |
||
4489 | * Is this user's e-mail address valid-looking and confirmed within |
||
4490 | * limits of the current site configuration? |
||
4491 | * |
||
4492 | * @note If $wgEmailAuthentication is on, this may require the user to have |
||
4493 | * confirmed their address by returning a code or using a password |
||
4494 | * sent to the address from the wiki. |
||
4495 | * |
||
4496 | * @return bool |
||
4497 | */ |
||
4498 | public function isEmailConfirmed() { |
||
4517 | |||
4518 | /** |
||
4519 | * Check whether there is an outstanding request for e-mail confirmation. |
||
4520 | * @return bool |
||
4521 | */ |
||
4522 | public function isEmailConfirmationPending() { |
||
4529 | |||
4530 | /** |
||
4531 | * Get the timestamp of account creation. |
||
4532 | * |
||
4533 | * @return string|bool|null Timestamp of account creation, false for |
||
4534 | * non-existent/anonymous user accounts, or null if existing account |
||
4535 | * but information is not in database. |
||
4536 | */ |
||
4537 | public function getRegistration() { |
||
4544 | |||
4545 | /** |
||
4546 | * Get the timestamp of the first edit |
||
4547 | * |
||
4548 | * @return string|bool Timestamp of first edit, or false for |
||
4549 | * non-existent/anonymous user accounts. |
||
4550 | */ |
||
4551 | public function getFirstEditTimestamp() { |
||
4566 | |||
4567 | /** |
||
4568 | * Get the permissions associated with a given list of groups |
||
4569 | * |
||
4570 | * @param array $groups Array of Strings List of internal group names |
||
4571 | * @return array Array of Strings List of permission key names for given groups combined |
||
4572 | */ |
||
4573 | public static function getGroupPermissions( $groups ) { |
||
4593 | |||
4594 | /** |
||
4595 | * Get all the groups who have a given permission |
||
4596 | * |
||
4597 | * @param string $role Role to check |
||
4598 | * @return array Array of Strings List of internal group names with the given permission |
||
4599 | */ |
||
4600 | public static function getGroupsWithPermission( $role ) { |
||
4610 | |||
4611 | /** |
||
4612 | * Check, if the given group has the given permission |
||
4613 | * |
||
4614 | * If you're wanting to check whether all users have a permission, use |
||
4615 | * User::isEveryoneAllowed() instead. That properly checks if it's revoked |
||
4616 | * from anyone. |
||
4617 | * |
||
4618 | * @since 1.21 |
||
4619 | * @param string $group Group to check |
||
4620 | * @param string $role Role to check |
||
4621 | * @return bool |
||
4622 | */ |
||
4623 | public static function groupHasPermission( $group, $role ) { |
||
4628 | |||
4629 | /** |
||
4630 | * Check if all users may be assumed to have the given permission |
||
4631 | * |
||
4632 | * We generally assume so if the right is granted to '*' and isn't revoked |
||
4633 | * on any group. It doesn't attempt to take grants or other extension |
||
4634 | * limitations on rights into account in the general case, though, as that |
||
4635 | * would require it to always return false and defeat the purpose. |
||
4636 | * Specifically, session-based rights restrictions (such as OAuth or bot |
||
4637 | * passwords) are applied based on the current session. |
||
4638 | * |
||
4639 | * @since 1.22 |
||
4640 | * @param string $right Right to check |
||
4641 | * @return bool |
||
4642 | */ |
||
4643 | public static function isEveryoneAllowed( $right ) { |
||
4685 | |||
4686 | /** |
||
4687 | * Get the localized descriptive name for a group, if it exists |
||
4688 | * |
||
4689 | * @param string $group Internal group name |
||
4690 | * @return string Localized descriptive group name |
||
4691 | */ |
||
4692 | public static function getGroupName( $group ) { |
||
4696 | |||
4697 | /** |
||
4698 | * Get the localized descriptive name for a member of a group, if it exists |
||
4699 | * |
||
4700 | * @param string $group Internal group name |
||
4701 | * @param string $username Username for gender (since 1.19) |
||
4702 | * @return string Localized name for group member |
||
4703 | */ |
||
4704 | public static function getGroupMember( $group, $username = '#' ) { |
||
4708 | |||
4709 | /** |
||
4710 | * Return the set of defined explicit groups. |
||
4711 | * The implicit groups (by default *, 'user' and 'autoconfirmed') |
||
4712 | * are not included, as they are defined automatically, not in the database. |
||
4713 | * @return array Array of internal group names |
||
4714 | */ |
||
4715 | public static function getAllGroups() { |
||
4722 | |||
4723 | /** |
||
4724 | * Get a list of all available permissions. |
||
4725 | * @return string[] Array of permission names |
||
4726 | */ |
||
4727 | public static function getAllRights() { |
||
4739 | |||
4740 | /** |
||
4741 | * Get a list of implicit groups |
||
4742 | * @return array Array of Strings Array of internal group names |
||
4743 | */ |
||
4744 | public static function getImplicitGroups() { |
||
4753 | |||
4754 | /** |
||
4755 | * Get the title of a page describing a particular group |
||
4756 | * |
||
4757 | * @param string $group Internal group name |
||
4758 | * @return Title|bool Title of the page if it exists, false otherwise |
||
4759 | */ |
||
4760 | public static function getGroupPage( $group ) { |
||
4770 | |||
4771 | /** |
||
4772 | * Create a link to the group in HTML, if available; |
||
4773 | * else return the group name. |
||
4774 | * |
||
4775 | * @param string $group Internal name of the group |
||
4776 | * @param string $text The text of the link |
||
4777 | * @return string HTML link to the group |
||
4778 | */ |
||
4779 | public static function makeGroupLinkHTML( $group, $text = '' ) { |
||
4790 | |||
4791 | /** |
||
4792 | * Create a link to the group in Wikitext, if available; |
||
4793 | * else return the group name. |
||
4794 | * |
||
4795 | * @param string $group Internal name of the group |
||
4796 | * @param string $text The text of the link |
||
4797 | * @return string Wikilink to the group |
||
4798 | */ |
||
4799 | public static function makeGroupLinkWiki( $group, $text = '' ) { |
||
4811 | |||
4812 | /** |
||
4813 | * Returns an array of the groups that a particular group can add/remove. |
||
4814 | * |
||
4815 | * @param string $group The group to check for whether it can add/remove |
||
4816 | * @return array Array( 'add' => array( addablegroups ), |
||
4817 | * 'remove' => array( removablegroups ), |
||
4818 | * 'add-self' => array( addablegroups to self), |
||
4819 | * 'remove-self' => array( removable groups from self) ) |
||
4820 | */ |
||
4821 | public static function changeableByGroup( $group ) { |
||
4886 | |||
4887 | /** |
||
4888 | * Returns an array of groups that this user can add and remove |
||
4889 | * @return array Array( 'add' => array( addablegroups ), |
||
4890 | * 'remove' => array( removablegroups ), |
||
4891 | * 'add-self' => array( addablegroups to self), |
||
4892 | * 'remove-self' => array( removable groups from self) ) |
||
4893 | */ |
||
4894 | public function changeableGroups() { |
||
4929 | |||
4930 | /** |
||
4931 | * Deferred version of incEditCountImmediate() |
||
4932 | */ |
||
4933 | public function incEditCount() { |
||
4941 | |||
4942 | /** |
||
4943 | * Increment the user's edit-count field. |
||
4944 | * Will have no effect for anonymous users. |
||
4945 | * @since 1.26 |
||
4946 | */ |
||
4947 | public function incEditCountImmediate() { |
||
4987 | |||
4988 | /** |
||
4989 | * Initialize user_editcount from data out of the revision table |
||
4990 | * |
||
4991 | * @param int $add Edits to add to the count from the revision table |
||
4992 | * @return int Number of edits |
||
4993 | */ |
||
4994 | protected function initEditCount( $add = 0 ) { |
||
5016 | |||
5017 | /** |
||
5018 | * Get the description of a given right |
||
5019 | * |
||
5020 | * @param string $right Right to query |
||
5021 | * @return string Localized description of the right |
||
5022 | */ |
||
5023 | public static function getRightDescription( $right ) { |
||
5028 | |||
5029 | /** |
||
5030 | * Make a new-style password hash |
||
5031 | * |
||
5032 | * @param string $password Plain-text password |
||
5033 | * @param bool|string $salt Optional salt, may be random or the user ID. |
||
5034 | * If unspecified or false, will generate one automatically |
||
5035 | * @return string Password hash |
||
5036 | * @deprecated since 1.24, use Password class |
||
5037 | */ |
||
5038 | public static function crypt( $password, $salt = false ) { |
||
5045 | |||
5046 | /** |
||
5047 | * Compare a password hash with a plain-text password. Requires the user |
||
5048 | * ID if there's a chance that the hash is an old-style hash. |
||
5049 | * |
||
5050 | * @param string $hash Password hash |
||
5051 | * @param string $password Plain-text password to compare |
||
5052 | * @param string|bool $userId User ID for old-style password salt |
||
5053 | * |
||
5054 | * @return bool |
||
5055 | * @deprecated since 1.24, use Password class |
||
5056 | */ |
||
5057 | public static function comparePasswords( $hash, $password, $userId = false ) { |
||
5076 | |||
5077 | /** |
||
5078 | * Add a newuser log entry for this user. |
||
5079 | * Before 1.19 the return value was always true. |
||
5080 | * |
||
5081 | * @deprecated since 1.27, AuthManager handles logging |
||
5082 | * @param string|bool $action Account creation type. |
||
5083 | * - String, one of the following values: |
||
5084 | * - 'create' for an anonymous user creating an account for himself. |
||
5085 | * This will force the action's performer to be the created user itself, |
||
5086 | * no matter the value of $wgUser |
||
5087 | * - 'create2' for a logged in user creating an account for someone else |
||
5088 | * - 'byemail' when the created user will receive its password by e-mail |
||
5089 | * - 'autocreate' when the user is automatically created (such as by CentralAuth). |
||
5090 | * - Boolean means whether the account was created by e-mail (deprecated): |
||
5091 | * - true will be converted to 'byemail' |
||
5092 | * - false will be converted to 'create' if this object is the same as |
||
5093 | * $wgUser and to 'create2' otherwise |
||
5094 | * @param string $reason User supplied reason |
||
5095 | * @return bool true |
||
5096 | */ |
||
5097 | public function addNewUserLogEntry( $action = false, $reason = '' ) { |
||
5100 | |||
5101 | /** |
||
5102 | * Add an autocreate newuser log entry for this user |
||
5103 | * Used by things like CentralAuth and perhaps other authplugins. |
||
5104 | * Consider calling addNewUserLogEntry() directly instead. |
||
5105 | * |
||
5106 | * @deprecated since 1.27, AuthManager handles logging |
||
5107 | * @return bool |
||
5108 | */ |
||
5109 | public function addNewUserLogEntryAutoCreate() { |
||
5114 | |||
5115 | /** |
||
5116 | * Load the user options either from cache, the database or an array |
||
5117 | * |
||
5118 | * @param array $data Rows for the current user out of the user_properties table |
||
5119 | */ |
||
5120 | protected function loadOptions( $data = null ) { |
||
5180 | |||
5181 | /** |
||
5182 | * Saves the non-default options for this user, as previously set e.g. via |
||
5183 | * setOption(), in the database's "user_properties" (preferences) table. |
||
5184 | * Usually used via saveSettings(). |
||
5185 | */ |
||
5186 | protected function saveOptions() { |
||
5245 | |||
5246 | /** |
||
5247 | * Lazily instantiate and return a factory object for making passwords |
||
5248 | * |
||
5249 | * @deprecated since 1.27, create a PasswordFactory directly instead |
||
5250 | * @return PasswordFactory |
||
5251 | */ |
||
5252 | public static function getPasswordFactory() { |
||
5258 | |||
5259 | /** |
||
5260 | * Provide an array of HTML5 attributes to put on an input element |
||
5261 | * intended for the user to enter a new password. This may include |
||
5262 | * required, title, and/or pattern, depending on $wgMinimalPasswordLength. |
||
5263 | * |
||
5264 | * Do *not* use this when asking the user to enter his current password! |
||
5265 | * Regardless of configuration, users may have invalid passwords for whatever |
||
5266 | * reason (e.g., they were set before requirements were tightened up). |
||
5267 | * Only use it when asking for a new password, like on account creation or |
||
5268 | * ResetPass. |
||
5269 | * |
||
5270 | * Obviously, you still need to do server-side checking. |
||
5271 | * |
||
5272 | * NOTE: A combination of bugs in various browsers means that this function |
||
5273 | * actually just returns array() unconditionally at the moment. May as |
||
5274 | * well keep it around for when the browser bugs get fixed, though. |
||
5275 | * |
||
5276 | * @todo FIXME: This does not belong here; put it in Html or Linker or somewhere |
||
5277 | * |
||
5278 | * @deprecated since 1.27 |
||
5279 | * @return array Array of HTML attributes suitable for feeding to |
||
5280 | * Html::element(), directly or indirectly. (Don't feed to Xml::*()! |
||
5281 | * That will get confused by the boolean attribute syntax used.) |
||
5282 | */ |
||
5283 | public static function passwordChangeInputAttribs() { |
||
5317 | |||
5318 | /** |
||
5319 | * Return the list of user fields that should be selected to create |
||
5320 | * a new user object. |
||
5321 | * @return array |
||
5322 | */ |
||
5323 | public static function selectFields() { |
||
5338 | |||
5339 | /** |
||
5340 | * Factory function for fatal permission-denied errors |
||
5341 | * |
||
5342 | * @since 1.22 |
||
5343 | * @param string $permission User right required |
||
5344 | * @return Status |
||
5345 | */ |
||
5346 | static function newFatalPermissionDeniedStatus( $permission ) { |
||
5360 | |||
5361 | /** |
||
5362 | * Get a new instance of this user that was loaded from the master via a locking read |
||
5363 | * |
||
5364 | * Use this instead of the main context User when updating that user. This avoids races |
||
5365 | * where that user was loaded from a replica DB or even the master but without proper locks. |
||
5366 | * |
||
5367 | * @return User|null Returns null if the user was not found in the DB |
||
5368 | * @since 1.27 |
||
5369 | */ |
||
5370 | public function getInstanceForUpdate() { |
||
5382 | |||
5383 | /** |
||
5384 | * Checks if two user objects point to the same user. |
||
5385 | * |
||
5386 | * @since 1.25 |
||
5387 | * @param User $user |
||
5388 | * @return bool |
||
5389 | */ |
||
5390 | public function equals( User $user ) { |
||
5393 | } |
||
5394 |
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: