1: <?php
2: /**
3: * ElggUser
4: *
5: * Representation of a "user" in the system.
6: *
7: * @package Elgg.Core
8: * @subpackage DataModel.User
9: *
10: * @property string $name The display name that the user will be known by in the network
11: * @property string $username The short, reference name for the user in the network
12: * @property string $email The email address to which Elgg will send email notifications
13: * @property string $language The language preference of the user (ISO 639-1 formatted)
14: * @property string $banned 'yes' if the user is banned from the network, 'no' otherwise
15: * @property string $admin 'yes' if the user is an administrator of the network, 'no' otherwise
16: * @property string $password The hashed password of the user
17: * @property string $salt The salt used to secure the password before hashing
18: */
19: class ElggUser extends ElggEntity
20: implements Friendable {
21:
22: /**
23: * Initialise the attributes array.
24: * This is vital to distinguish between metadata and base parameters.
25: *
26: * Place your base parameters here.
27: *
28: * @return void
29: */
30: protected function initializeAttributes() {
31: parent::initializeAttributes();
32:
33: $this->attributes['type'] = "user";
34: $this->attributes['name'] = NULL;
35: $this->attributes['username'] = NULL;
36: $this->attributes['password'] = NULL;
37: $this->attributes['salt'] = NULL;
38: $this->attributes['email'] = NULL;
39: $this->attributes['language'] = NULL;
40: $this->attributes['code'] = NULL;
41: $this->attributes['banned'] = "no";
42: $this->attributes['admin'] = 'no';
43: $this->attributes['prev_last_action'] = NULL;
44: $this->attributes['last_login'] = NULL;
45: $this->attributes['prev_last_login'] = NULL;
46: $this->attributes['tables_split'] = 2;
47: }
48:
49: /**
50: * Construct a new user entity, optionally from a given id value.
51: *
52: * @param mixed $guid If an int, load that GUID.
53: * If an entity table db row then will load the rest of the data.
54: *
55: * @throws Exception if there was a problem creating the user.
56: */
57: function __construct($guid = null) {
58: $this->initializeAttributes();
59:
60: // compatibility for 1.7 api.
61: $this->initialise_attributes(false);
62:
63: if (!empty($guid)) {
64: // Is $guid is a DB entity row
65: if ($guid instanceof stdClass) {
66: // Load the rest
67: if (!$this->load($guid)) {
68: $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
69: throw new IOException($msg);
70: }
71: } else if (is_string($guid)) {
72: // $guid is a username
73: $user = get_user_by_username($guid);
74: if ($user) {
75: foreach ($user->attributes as $key => $value) {
76: $this->attributes[$key] = $value;
77: }
78: }
79: } else if ($guid instanceof ElggUser) {
80: // $guid is an ElggUser so this is a copy constructor
81: elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
82:
83: foreach ($guid->attributes as $key => $value) {
84: $this->attributes[$key] = $value;
85: }
86: } else if ($guid instanceof ElggEntity) {
87: // @todo why have a special case here
88: throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
89: } else if (is_numeric($guid)) {
90: // $guid is a GUID so load entity
91: if (!$this->load($guid)) {
92: throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
93: }
94: } else {
95: throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
96: }
97: }
98: }
99:
100: /**
101: * Load the ElggUser data from the database
102: *
103: * @param mixed $guid ElggUser GUID or stdClass database row from entity table
104: *
105: * @return bool
106: */
107: protected function load($guid) {
108: $attr_loader = new ElggAttributeLoader(get_class(), 'user', $this->attributes);
109: $attr_loader->secondary_loader = 'get_user_entity_as_row';
110:
111: $attrs = $attr_loader->getRequiredAttributes($guid);
112: if (!$attrs) {
113: return false;
114: }
115:
116: $this->attributes = $attrs;
117: $this->attributes['tables_loaded'] = 2;
118: _elgg_cache_entity($this);
119:
120: return true;
121: }
122:
123: /**
124: * Saves this user to the database.
125: *
126: * @return bool
127: */
128: public function save() {
129: // Save generic stuff
130: if (!parent::save()) {
131: return false;
132: }
133:
134: // Now save specific stuff
135: _elgg_disable_caching_for_entity($this->guid);
136: $ret = create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
137: $this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'),
138: $this->get('code'));
139: _elgg_enable_caching_for_entity($this->guid);
140:
141: return $ret;
142: }
143:
144: /**
145: * User specific override of the entity delete method.
146: *
147: * @return bool
148: */
149: public function delete() {
150: global $USERNAME_TO_GUID_MAP_CACHE, $CODE_TO_GUID_MAP_CACHE;
151:
152: // clear cache
153: if (isset($USERNAME_TO_GUID_MAP_CACHE[$this->username])) {
154: unset($USERNAME_TO_GUID_MAP_CACHE[$this->username]);
155: }
156: if (isset($CODE_TO_GUID_MAP_CACHE[$this->code])) {
157: unset($CODE_TO_GUID_MAP_CACHE[$this->code]);
158: }
159:
160: clear_user_files($this);
161:
162: // Delete entity
163: return parent::delete();
164: }
165:
166: /**
167: * Ban this user.
168: *
169: * @param string $reason Optional reason
170: *
171: * @return bool
172: */
173: public function ban($reason = "") {
174: return ban_user($this->guid, $reason);
175: }
176:
177: /**
178: * Unban this user.
179: *
180: * @return bool
181: */
182: public function unban() {
183: return unban_user($this->guid);
184: }
185:
186: /**
187: * Is this user banned or not?
188: *
189: * @return bool
190: */
191: public function isBanned() {
192: return $this->banned == 'yes';
193: }
194:
195: /**
196: * Is this user admin?
197: *
198: * @return bool
199: */
200: public function isAdmin() {
201:
202: // for backward compatibility we need to pull this directly
203: // from the attributes instead of using the magic methods.
204: // this can be removed in 1.9
205: // return $this->admin == 'yes';
206: return $this->attributes['admin'] == 'yes';
207: }
208:
209: /**
210: * Make the user an admin
211: *
212: * @return bool
213: */
214: public function makeAdmin() {
215: // If already saved, use the standard function.
216: if ($this->guid && !make_user_admin($this->guid)) {
217: return FALSE;
218: }
219:
220: // need to manually set attributes since they've already been loaded.
221: $this->attributes['admin'] = 'yes';
222:
223: return TRUE;
224: }
225:
226: /**
227: * Remove the admin flag for user
228: *
229: * @return bool
230: */
231: public function removeAdmin() {
232: // If already saved, use the standard function.
233: if ($this->guid && !remove_user_admin($this->guid)) {
234: return FALSE;
235: }
236:
237: // need to manually set attributes since they've already been loaded.
238: $this->attributes['admin'] = 'no';
239:
240: return TRUE;
241: }
242:
243: /**
244: * Get sites that this user is a member of
245: *
246: * @param string $subtype Optionally, the subtype of result we want to limit to
247: * @param int $limit The number of results to return
248: * @param int $offset Any indexing offset
249: *
250: * @return array
251: */
252: function getSites($subtype = "", $limit = 10, $offset = 0) {
253: return get_user_sites($this->getGUID(), $subtype, $limit, $offset);
254: }
255:
256: /**
257: * Add this user to a particular site
258: *
259: * @param int $site_guid The guid of the site to add it to
260: *
261: * @return bool
262: */
263: function addToSite($site_guid) {
264: return add_site_user($site_guid, $this->getGUID());
265: }
266:
267: /**
268: * Remove this user from a particular site
269: *
270: * @param int $site_guid The guid of the site to remove it from
271: *
272: * @return bool
273: */
274: function removeFromSite($site_guid) {
275: return remove_site_user($site_guid, $this->getGUID());
276: }
277:
278: /**
279: * Adds a user as a friend
280: *
281: * @param int $friend_guid The GUID of the user to add
282: *
283: * @return bool
284: */
285: function addFriend($friend_guid) {
286: return user_add_friend($this->getGUID(), $friend_guid);
287: }
288:
289: /**
290: * Removes a user as a friend
291: *
292: * @param int $friend_guid The GUID of the user to remove
293: *
294: * @return bool
295: */
296: function removeFriend($friend_guid) {
297: return user_remove_friend($this->getGUID(), $friend_guid);
298: }
299:
300: /**
301: * Determines whether or not this user is a friend of the currently logged in user
302: *
303: * @return bool
304: */
305: function isFriend() {
306: return $this->isFriendOf(elgg_get_logged_in_user_guid());
307: }
308:
309: /**
310: * Determines whether this user is friends with another user
311: *
312: * @param int $user_guid The GUID of the user to check against
313: *
314: * @return bool
315: */
316: function isFriendsWith($user_guid) {
317: return user_is_friend($this->getGUID(), $user_guid);
318: }
319:
320: /**
321: * Determines whether or not this user is another user's friend
322: *
323: * @param int $user_guid The GUID of the user to check against
324: *
325: * @return bool
326: */
327: function isFriendOf($user_guid) {
328: return user_is_friend($user_guid, $this->getGUID());
329: }
330:
331: /**
332: * Gets this user's friends
333: *
334: * @param string $subtype Optionally, the user subtype (leave blank for all)
335: * @param int $limit The number of users to retrieve
336: * @param int $offset Indexing offset, if any
337: *
338: * @return array|false Array of ElggUser, or false, depending on success
339: */
340: function getFriends($subtype = "", $limit = 10, $offset = 0) {
341: return get_user_friends($this->getGUID(), $subtype, $limit, $offset);
342: }
343:
344: /**
345: * Gets users who have made this user a friend
346: *
347: * @param string $subtype Optionally, the user subtype (leave blank for all)
348: * @param int $limit The number of users to retrieve
349: * @param int $offset Indexing offset, if any
350: *
351: * @return array|false Array of ElggUser, or false, depending on success
352: */
353: function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
354: return get_user_friends_of($this->getGUID(), $subtype, $limit, $offset);
355: }
356:
357: /**
358: * Lists the user's friends
359: *
360: * @param string $subtype Optionally, the user subtype (leave blank for all)
361: * @param int $limit The number of users to retrieve
362: * @param array $vars Display variables for the user view
363: *
364: * @return string Rendered list of friends
365: * @since 1.8.0
366: */
367: function listFriends($subtype = "", $limit = 10, array $vars = array()) {
368: $defaults = array(
369: 'type' => 'user',
370: 'relationship' => 'friend',
371: 'relationship_guid' => $this->guid,
372: 'limit' => $limit,
373: 'full_view' => false,
374: );
375:
376: $options = array_merge($defaults, $vars);
377:
378: if ($subtype) {
379: $options['subtype'] = $subtype;
380: }
381:
382: return elgg_list_entities_from_relationship($options);
383: }
384:
385: /**
386: * Gets the user's groups
387: *
388: * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
389: * @param int $limit The number of groups to retrieve
390: * @param int $offset Indexing offset, if any
391: *
392: * @return array|false Array of ElggGroup, or false, depending on success
393: */
394: function getGroups($subtype = "", $limit = 10, $offset = 0) {
395: $options = array(
396: 'type' => 'group',
397: 'relationship' => 'member',
398: 'relationship_guid' => $this->guid,
399: 'limit' => $limit,
400: 'offset' => $offset,
401: );
402:
403: if ($subtype) {
404: $options['subtype'] = $subtype;
405: }
406:
407: return elgg_get_entities_from_relationship($options);
408: }
409:
410: /**
411: * Lists the user's groups
412: *
413: * @param string $subtype Optionally, the user subtype (leave blank for all)
414: * @param int $limit The number of users to retrieve
415: * @param int $offset Indexing offset, if any
416: *
417: * @return string
418: */
419: function listGroups($subtype = "", $limit = 10, $offset = 0) {
420: $options = array(
421: 'type' => 'group',
422: 'relationship' => 'member',
423: 'relationship_guid' => $this->guid,
424: 'limit' => $limit,
425: 'offset' => $offset,
426: 'full_view' => false,
427: );
428:
429: if ($subtype) {
430: $options['subtype'] = $subtype;
431: }
432:
433: return elgg_list_entities_from_relationship($options);
434: }
435:
436: /**
437: * Get an array of ElggObject owned by this user.
438: *
439: * @param string $subtype The subtype of the objects, if any
440: * @param int $limit Number of results to return
441: * @param int $offset Any indexing offset
442: *
443: * @return array|false
444: */
445: public function getObjects($subtype = "", $limit = 10, $offset = 0) {
446: $params = array(
447: 'type' => 'object',
448: 'subtype' => $subtype,
449: 'owner_guid' => $this->getGUID(),
450: 'limit' => $limit,
451: 'offset' => $offset
452: );
453: return elgg_get_entities($params);
454: }
455:
456: /**
457: * Get an array of ElggObjects owned by this user's friends.
458: *
459: * @param string $subtype The subtype of the objects, if any
460: * @param int $limit Number of results to return
461: * @param int $offset Any indexing offset
462: *
463: * @return array|false
464: */
465: public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
466: return get_user_friends_objects($this->getGUID(), $subtype, $limit, $offset);
467: }
468:
469: /**
470: * Counts the number of ElggObjects owned by this user
471: *
472: * @param string $subtype The subtypes of the objects, if any
473: *
474: * @return int The number of ElggObjects
475: */
476: public function countObjects($subtype = "") {
477: return count_user_objects($this->getGUID(), $subtype);
478: }
479:
480: /**
481: * Get the collections associated with a user.
482: *
483: * @param string $subtype Optionally, the subtype of result we want to limit to
484: * @param int $limit The number of results to return
485: * @param int $offset Any indexing offset
486: *
487: * @return array|false
488: */
489: public function getCollections($subtype = "", $limit = 10, $offset = 0) {
490: elgg_deprecated_notice("ElggUser::getCollections() has been deprecated", 1.8);
491: return false;
492: }
493:
494: /**
495: * Get a user's owner GUID
496: *
497: * Returns it's own GUID if the user is not owned.
498: *
499: * @return int
500: */
501: function getOwnerGUID() {
502: if ($this->owner_guid == 0) {
503: return $this->guid;
504: }
505:
506: return $this->owner_guid;
507: }
508:
509: /**
510: * If a user's owner is blank, return its own GUID as the owner
511: *
512: * @return int User GUID
513: * @deprecated 1.8 Use getOwnerGUID()
514: */
515: function getOwner() {
516: elgg_deprecated_notice("ElggUser::getOwner deprecated for ElggUser::getOwnerGUID", 1.8);
517: $this->getOwnerGUID();
518: }
519:
520: // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
521:
522: /**
523: * Return an array of fields which can be exported.
524: *
525: * @return array
526: */
527: public function getExportableValues() {
528: return array_merge(parent::getExportableValues(), array(
529: 'name',
530: 'username',
531: 'language',
532: ));
533: }
534:
535: /**
536: * Need to catch attempts to make a user an admin. Remove for 1.9
537: *
538: * @param string $name Name
539: * @param mixed $value Value
540: *
541: * @return bool
542: */
543: public function __set($name, $value) {
544: if ($name == 'admin' || $name == 'siteadmin') {
545: elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->makeAdmin() and ElggUser->removeAdmin().', 1.7);
546:
547: if ($value == 'yes' || $value == '1') {
548: $this->makeAdmin();
549: } else {
550: $this->removeAdmin();
551: }
552: }
553: return parent::__set($name, $value);
554: }
555:
556: /**
557: * Need to catch attempts to test user for admin. Remove for 1.9
558: *
559: * @param string $name Name
560: *
561: * @return bool
562: */
563: public function __get($name) {
564: if ($name == 'admin' || $name == 'siteadmin') {
565: elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->isAdmin().', 1.7);
566: return $this->isAdmin();
567: }
568:
569: return parent::__get($name);
570: }
571:
572: /**
573: * Can a user comment on this user?
574: *
575: * @see ElggEntity::canComment()
576: *
577: * @param int $user_guid User guid (default is logged in user)
578: * @return bool
579: * @since 1.8.0
580: */
581: public function canComment($user_guid = 0) {
582: $result = parent::canComment($user_guid);
583: if ($result !== null) {
584: return $result;
585: }
586: return false;
587: }
588: }
589: