Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/ElggUser.php (6 issues)

1
<?php
2
/**
3
 * A user entity
4
 *
5
 * @property      string $name             The display name that the user will be known by in the network
6
 * @property      string $username         The short, reference name for the user in the network
7
 * @property      string $email            The email address to which Elgg will send email notifications
8
 * @property      string $language         The language preference of the user (ISO 639-1 formatted)
9
 * @property      string $banned           'yes' if the user is banned from the network, 'no' otherwise
10
 * @property      string $admin            'yes' if the user is an administrator of the network, 'no' otherwise
11
 * @property-read string $password_hash    The hashed password of the user
12
 * @property-read int    $prev_last_action A UNIX timestamp of the previous last action
13
 * @property-read int    $last_login       A UNIX timestamp of the last login
14
 * @property-read int    $prev_last_login  A UNIX timestamp of the previous login
15
 */
16
class ElggUser extends \ElggEntity
17
	implements Friendable {
18
19
	/**
20
	 * {@inheritdoc}
21
	 */
22 828
	protected function initializeAttributes() {
23 828
		parent::initializeAttributes();
24 828
		$this->attributes['subtype'] = 'user';
25 828
	}
26
27
	/**
28
	 * {@inheritdoc}
29
	 */
30 828
	public function getType() {
31 828
		return 'user';
32
	}
33
	
34
	/**
35
	 * Get user language or default to site language
36
	 *
37
	 * @param string $fallback If this is provided, it will be returned if the user doesn't have a language set.
38
	 *                         If null, the site language will be returned.
39
	 *
40
	 * @return string
41
	 */
42 4
	public function getLanguage($fallback = null) {
43 4
		if (!empty($this->language)) {
44 4
			return $this->language;
45
		}
46
		if ($fallback !== null) {
47
			return $fallback;
48
		}
49
		return elgg_get_config('language');
50
	}
51
52
	/**
53
	 * {@inheritdoc}
54
	 */
55 458
	public function __set($name, $value) {
56 458
		switch ($name) {
57
			case 'salt':
58
			case 'password':
59
				_elgg_services()->logger->error("User entities no longer contain {$name}");
60
				return;
61
			case 'password_hash':
62
				_elgg_services()->logger->error("password_hash is a readonly attribute.");
63
				return;
64
			case 'email':
65 447
				if (!validate_email_address($value)) {
66
					throw new \InvalidParameterException("Email is not a valid email address");
67
				}
68 447
				break;
69
			case 'username':
70 458
				if (!validate_username($value)) {
71
					throw new \InvalidParameterException("Username is not a valid username");
72
				}
73 458
				$existing_user = get_user_by_username($value);
74 458
				if ($existing_user && ($existing_user->guid !== $this->guid)) {
75
					throw new \InvalidParameterException("{$name} is supposed to be unique for ElggUser");
76
				}
77 458
				break;
78
		}
79
		
80 458
		parent::__set($name, $value);
81 458
	}
82
	
83
	/**
84
	 * {@inheritdoc}
85
	 */
86 15
	public function getURL() {
87
		
88 15
		$result = parent::getURL();
89 15
		if ($result !== '') {
90 1
			return $result;
91
		}
92
		
93 14
		return elgg_normalize_url("user/view/{$this->guid}");
94
	}
95
96
	/**
97
	 * Ban this user.
98
	 *
99
	 * @param string $reason Optional reason
100
	 *
101
	 * @return bool
102
	 */
103 73
	public function ban($reason = '') {
104
105 73
		if (!$this->canEdit()) {
106
			return false;
107
		}
108
		
109 73
		if (!elgg_trigger_event('ban', 'user', $this)) {
110
			return false;
111
		}
112
113 73
		$this->ban_reason = $reason;
0 ignored issues
show
Bug Best Practice introduced by
The property ban_reason does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
114 73
		$this->banned = 'yes';
115
				
116 73
		$this->invalidateCache();
117
118 73
		return true;
119
	}
120
121
	/**
122
	 * Unban this user.
123
	 *
124
	 * @return bool
125
	 */
126 2
	public function unban() {
127
		
128 2
		if (!$this->canEdit()) {
129
			return false;
130
		}
131
132 2
		if (!elgg_trigger_event('unban', 'user', $this)) {
133
			return false;
134
		}
135
136 2
		unset($this->ban_reason);
137 2
		$this->banned = 'yes';
138
				
139 2
		$this->invalidateCache();
140
141 2
		return true;
142
	}
143
144
	/**
145
	 * Is this user banned or not?
146
	 *
147
	 * @return bool
148
	 */
149 30
	public function isBanned() {
150 30
		return $this->banned == 'yes';
151
	}
152
153
	/**
154
	 * Is this user admin?
155
	 *
156
	 * @return bool
157
	 */
158 375
	public function isAdmin() {
159 375
		$ia = elgg_set_ignore_access(true);
160 375
		$is_admin = ($this->admin == 'yes');
161 375
		elgg_set_ignore_access($ia);
162
		
163 375
		return $is_admin;
164
	}
165
166
	/**
167
	 * Make the user an admin
168
	 *
169
	 * @return bool
170
	 */
171 7
	public function makeAdmin() {
172
		
173 7
		if ($this->isAdmin()) {
174
			return true;
175
		}
176
177 7
		if (!elgg_trigger_event('make_admin', 'user', $this)) {
178
			return false;
179
		}
180
181 7
		$this->admin = 'yes';
182
183 7
		$this->invalidateCache();
184
		
185 7
		return true;
186
	}
187
188
	/**
189
	 * Remove the admin flag for user
190
	 *
191
	 * @return bool
192
	 */
193 3
	public function removeAdmin() {
194
195 3
		if (!$this->isAdmin()) {
196 1
			return true;
197
		}
198
199 2
		if (!elgg_trigger_event('remove_admin', 'user', $this)) {
200
			return false;
201
		}
202
203 2
		$this->admin = 'no';
204
205 2
		$this->invalidateCache();
206
		
207 2
		return true;
208
	}
209
	
210
	/**
211
	 * Sets the last logon time of the user to right now.
212
	 *
213
	 * @return void
214
	 */
215 4
	public function setLastLogin() {
216
		
217 4
		$time = $this->getCurrentTime()->getTimestamp();
218
		
219 4
		if ($this->last_login == $time) {
220
			// no change required
221
			return;
222
		}
223
		
224
		// these writes actually work, we just type hint read-only.
225 4
		$this->prev_last_login = $this->last_login;
1 ignored issue
show
The property prev_last_login is declared read-only in ElggUser.
Loading history...
226 4
		$this->last_login = $time;
1 ignored issue
show
The property last_login is declared read-only in ElggUser.
Loading history...
227 4
	}
228
	
229
	/**
230
	 * Sets the last action time of the given user to right now.
231
	 *
232
	 * @see _elgg_session_boot The session boot calls this at the beginning of every request
233
	 *
234
	 * @return void
235
	 */
236
	public function setLastAction() {
237
		
238
		$time = $this->getCurrentTime()->getTimestamp();
239
		
240
		if ($this->last_action == $time) {
241
			// no change required
242
			return;
243
		}
244
		
245
		// these writes actually work, we just type hint read-only.
246
		$this->prev_last_action = $this->last_action;
0 ignored issues
show
The property prev_last_action is declared read-only in ElggUser.
Loading history...
247
		
248
		$this->updateLastAction($time);
249
	}
250
	
251
	/**
252
	 * Gets the validation status of a user.
253
	 *
254
	 * @return bool|null Null means status was not set for this user.
255
	 */
256 6
	public function isValidated() {
257 6
		if (!isset($this->validated)) {
258 6
			return null;
259
		}
260
		return (bool) $this->validated;
261
	}
262
	
263
	/**
264
	 * Set the validation status for a user.
265
	 *
266
	 * @param bool   $status    Validated (true) or unvalidated (false)
267
	 * @param string $method    Optional method to say how a user was validated
268
	 * @return void
269
	 */
270 165
	public function setValidationStatus($status, $method = '') {
271
		
272 165
		$this->validated = $status;
0 ignored issues
show
Bug Best Practice introduced by
The property validated does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
273 165
		$this->validated_method = $method;
0 ignored issues
show
Bug Best Practice introduced by
The property validated_method does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
274
		
275 165
		if ((bool) $status) {
276 103
			elgg_trigger_after_event('validate', 'user', $this);
277
		} else {
278 77
			elgg_trigger_after_event('invalidate', 'user', $this);
279
		}
280 165
	}
281
282
	/**
283
	 * Adds a user as a friend
284
	 *
285
	 * @param int  $friend_guid       The GUID of the user to add
286
	 * @param bool $create_river_item Create the river item announcing this friendship
287
	 *
288
	 * @return bool
289
	 */
290
	public function addFriend($friend_guid, $create_river_item = false) {
291
		if (!get_user($friend_guid)) {
292
			return false;
293
		}
294
295
		if (!add_entity_relationship($this->guid, "friend", $friend_guid)) {
296
			return false;
297
		}
298
299
		if ($create_river_item) {
300
			elgg_create_river_item([
301
				'view' => 'river/relationship/friend/create',
302
				'action_type' => 'friend',
303
				'subject_guid' => $this->guid,
304
				'object_guid' => $friend_guid,
305
			]);
306
		}
307
308
		return true;
309
	}
310
311
	/**
312
	 * Removes a user as a friend
313
	 *
314
	 * @param int $friend_guid The GUID of the user to remove
315
	 * @return bool
316
	 */
317
	public function removeFriend($friend_guid) {
318
		return $this->removeRelationship($friend_guid, 'friend');
319
	}
320
321
	/**
322
	 * Determines whether or not this user is a friend of the currently logged in user
323
	 *
324
	 * @return bool
325
	 */
326
	public function isFriend() {
327
		return $this->isFriendOf(_elgg_services()->session->getLoggedInUserGuid());
328
	}
329
330
	/**
331
	 * Determines whether this user is friends with another user
332
	 *
333
	 * @param int $user_guid The GUID of the user to check against
334
	 *
335
	 * @return bool
336
	 */
337
	public function isFriendsWith($user_guid) {
338
		return (bool) check_entity_relationship($this->guid, "friend", $user_guid);
339
	}
340
341
	/**
342
	 * Determines whether or not this user is another user's friend
343
	 *
344
	 * @param int $user_guid The GUID of the user to check against
345
	 *
346
	 * @return bool
347
	 */
348
	public function isFriendOf($user_guid) {
349
		return (bool) check_entity_relationship($user_guid, "friend", $this->guid);
350
	}
351
352
	/**
353
	 * {@inheritdoc}
354
	 */
355
	public function getFriends(array $options = []) {
356
		$options['relationship'] = 'friend';
357
		$options['relationship_guid'] = $this->getGUID();
358
		$options['type'] = 'user';
359
360
		return elgg_get_entities($options);
361
	}
362
363
	/**
364
	 * {@inheritdoc}
365
	 */
366
	public function getFriendsOf(array $options = []) {
367
		$options['relationship'] = 'friend';
368
		$options['relationship_guid'] = $this->getGUID();
369
		$options['inverse_relationship'] = true;
370
		$options['type'] = 'user';
371
372
		return elgg_get_entities($options);
373
	}
374
375
	/**
376
	 * Gets the user's groups
377
	 *
378
	 * @param array $options Options array.
379
	 *
380
	 * @return array|false Array of \ElggGroup, or false, depending on success
381
	 */
382
	public function getGroups(array $options = []) {
383
		$options['type'] = 'group';
384
		$options['relationship'] = 'member';
385
		$options['relationship_guid'] = $this->guid;
386
387
		return elgg_get_entities($options);
388
	}
389
390
	/**
391
	 * {@inheritdoc}
392
	 */
393
	public function getObjects(array $options = []) {
394
		$options['type'] = 'object';
395
		$options['owner_guid'] = $this->getGUID();
396
397
		return elgg_get_entities($options);
398
	}
399
400
	/**
401
	 * {@inheritdoc}
402
	 */
403
	public function getFriendsObjects(array $options = []) {
404
		$options['type'] = 'object';
405
		$options['relationship'] = 'friend';
406
		$options['relationship_guid'] = $this->getGUID();
407
		$options['relationship_join_on'] = 'container_guid';
408
409
		return elgg_get_entities($options);
410
	}
411
412
	/**
413
	 * Get a user's owner GUID
414
	 *
415
	 * Returns it's own GUID if the user is not owned.
416
	 *
417
	 * @return int
418
	 */
419 1
	public function getOwnerGUID() {
420 1
		if ($this->owner_guid == 0) {
421 1
			return $this->guid;
422
		}
423
424
		return $this->owner_guid;
425
	}
426
427
	/**
428
	 * {@inheritdoc}
429
	 */
430 1
	protected function prepareObject($object) {
431 1
		$object = parent::prepareObject($object);
432 1
		$object->name = $this->getDisplayName();
433 1
		$object->username = $this->username;
434 1
		$object->language = $this->language;
435 1
		unset($object->read_access);
436 1
		return $object;
437
	}
438
439
	/**
440
	 * Can a user comment on this user?
441
	 *
442
	 * @see \ElggEntity::canComment()
443
	 *
444
	 * @param int  $user_guid User guid (default is logged in user)
445
	 * @param bool $default   Default permission
446
	 * @return bool
447
	 * @since 1.8.0
448
	 */
449 2
	public function canComment($user_guid = 0, $default = null) {
450 2
		return false;
451
	}
452
453
	/**
454
	 * Set the necessary metadata to store a hash of the user's password.
455
	 *
456
	 * @param string $password The password to be hashed
457
	 * @return void
458
	 * @since 1.10.0
459
	 */
460 162
	public function setPassword($password) {
461 162
		$this->setMetadata('password_hash', _elgg_services()->passwords->generateHash($password));
462 162
	}
463
464
	/**
465
	 * Enable or disable a notification delivery method
466
	 *
467
	 * @param string $method  Method name
468
	 * @param bool   $enabled Enabled or disabled
469
	 * @return bool
470
	 */
471 187
	public function setNotificationSetting($method, $enabled = true) {
472 187
		$this->{"notification:method:$method"} = (int) $enabled;
473 187
		return (bool) $this->save();
474
	}
475
476
	/**
477
	 * Returns users's notification settings
478
	 * <code>
479
	 *    [
480
	 *       'email' => true, // enabled
481
	 *       'ajax' => false, // disabled
482
	 *    ]
483
	 * </code>
484
	 *
485
	 * @return array
486
	 */
487 21
	public function getNotificationSettings() {
488
489 21
		$settings = [];
490
491 21
		$methods = _elgg_services()->notifications->getMethods();
492 21
		foreach ($methods as $method) {
493 21
			$settings[$method] = (bool) $this->{"notification:method:$method"};
494
		}
495
496 21
		return $settings;
497
	
498
	}
499
500
	/**
501
	 * Cache the entity in a session and persisted caches
502
	 *
503
	 * @param bool $persist Store in persistent cache
504
	 *
505
	 * @return void
506
	 * @access private
507
	 * @internal
508
	 */
509 828
	public function cache($persist = true) {
510 828
		if ($persist && $this->username) {
511 828
			$tmp = $this->volatile;
512
513
			// don't store volatile data
514 828
			$this->volatile = [];
515
516 828
			_elgg_services()->dataCache->usernames->save($this->username, $this);
517
518 828
			$this->volatile = $tmp;
519
		}
520
521 828
		parent::cache($persist);
522 828
	}
523
524
	/**
525
	 * Invalidate cache for entity
526
	 *
527
	 * @return void
528
	 * @internal
529
	 */
530 457
	public function invalidateCache() {
531 457
		if ($this->username) {
532 457
			_elgg_services()->dataCache->usernames->delete($this->username);
533
		}
534
535 457
		parent::invalidateCache();
536 457
	}
537
}
538