Applied patch #411
[elgg.git] / lib / userlib.php
blobb3ed4f00edfc7f5eec5879b8e51e7e0ffba39f17
1 <?php
3 /**
4 * Library of functions for user polling and manipulation.
5 *
6 * @copyright Copyright (C) 2004-2006 Ben Werdmuller and David Tosh
7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8 * @package elgg
9 * @subpackage elgg.lib
12 // INITIALISATION //////////////////////////////////////////////////////////////
14 // TODO: These need somewhere else to live. They're to do with
15 // authentication and session management, not user management.
17 // Session variable name
18 define('user_session_name', 'elgguser');
20 // Persistent login cookie DEFs
21 define('AUTH_COOKIE', 'elggperm');
22 define('AUTH_COOKIE_LENGTH', 31556926); // 1YR in seconds
24 // Messages
25 define('AUTH_MSG_OK', __gettext("You have been logged on."));
26 define('AUTH_MSG_BADLOGIN', __gettext("Unrecognised username or password. The system could not log you on, or you may not have activated your account."));
27 define('AUTH_MSG_MISSING', __gettext("Either the username or password were not specified. The system could not log you on."));
29 /**
30 * User information value by id
32 * Returns a specified field by user id
34 * @param string $fieldname the specific field
35 * @param integer $user_id the user id
36 * @return mixed string if success else false if the user doesn't exist
37 * @author Ben WerdMuller <ben@curverider.co.uk>
39 function user_info($fieldname, $user_id) {
41 // Name table
42 static $id_to_name_table;
44 // Returns field from a given ID
46 $user_id = (int) $user_id;
48 if (!empty($user_id)) {
49 if (!isset($id_to_name_table[$user_id][$fieldname])) {
50 //$id_to_name_table[$user_id][$fieldname] = get_field('users',$fieldname,'ident',$user_id);
52 // this reduces number of db queries, but uses slightly more memory
53 // due to adodb's recordset generation, it has both named and numeric array keys
54 $id_to_name_table[$user_id] = (array) get_record('users','ident',$user_id);
56 if (isset($id_to_name_table[$user_id][$fieldname])) {
57 return $id_to_name_table[$user_id][$fieldname];
61 // If we've got here, the user didn't exist in the database
62 return false;
66 /**
67 * User information value by username
69 * Returns a specified field by username
71 * @param string $fieldname the specific field
72 * @param integer $user_id the user id
73 * @return mixed string if success else false if the user doesn't exist
74 * @author Ben WerdMuller <ben@curverider.co.uk>
76 function user_info_username($fieldname, $username) {
78 // Name table
79 static $name_to_id_table;
81 // Returns user's ID from a given name
83 if (!empty($username)) {
84 if (!isset($name_to_id_table[$username][$fieldname])) {
85 //$name_to_id_table[$username][$fieldname] = get_field('users',$fieldname,'username',$username);
86 $name_to_id_table[$username] = (array) get_record('users','username',$username);
88 if (isset($name_to_id_table[$username][$fieldname])){
89 return $name_to_id_table[$username][$fieldname];
93 // If we've got here, the user didn't exist in the database
94 return false;
98 /**
99 * Gets the type of a particular user
101 * @param integer $user_id the user id
102 * @return mixed string the user type or false if the user doesn't exist
103 * @author Ben WerdMuller <ben@curverider.co.uk>
105 function user_type($user_id) {
106 return user_info('user_type', $user_id);
110 * Returns a user's name, with event hooks allowing for interception.
111 * Internally passes around a "user_name" "display" event, with an object
112 * containing the elements 'name' and 'owner'.
114 * @uses $CFG
115 * @param integer $user_id The unique ID of the user we want to find the name for.
116 * @return string Returns the user's name, or a blank string if something went wrong (eg the user didn't exist).
117 * @author Ben WerdMuller <ben@curverider.co.uk>
119 function user_name($user_id) {
120 global $CFG;
121 $user_name = new stdClass;
122 $user_name->owner = $user_id;
123 if ($user_name->name = user_info("name",$user_id)) {
124 if ($user_name = plugin_hook("user_name","display",$user_name)) {
125 return $user_name->name;
128 return "";
132 * Returns the HTML to display a user's icon, with event hooks allowing for interception.
133 * Internally passes around a "user_icon" "display" event, with an object
134 * containing the elements 'html', 'icon' (being the icon ID), 'size', 'owner' and 'url'.
136 * @todo TODO refactor, separate display code
137 * @global CFG global configuration
138 * @param integer $user_id The unique ID of the user we want to display the icon for.
139 * @param integer $size The size of the icon we want to display (max: 100).
140 * @param boolean $urlonly If true, returns the URL of the icon rather than the full HTML.
141 * @return string Returns the icon HTML, or the default icon if something went wrong (eg the user didn't exist).
142 * @author Ben WerdMuller <ben@curverider.co.uk>
144 function user_icon_html($user_id, $size = 100, $urlonly = false) {
145 global $CFG;
146 $extra = "";
147 $user_icon = new stdClass;
148 $user_icon->owner = $user_id;
149 $user_icon->size = $size;
150 if ($size < 100) {
151 $extra = "/h/$size/w/$size";
153 if ($user_icon->icon = user_info("icon",$user_id)) {
154 $user_icon->url = "{$CFG->wwwroot}_icon/user/{$user_icon->icon}{$extra}";
155 $user_fullname = user_info("name",$user_id);
156 $user_icon->html = "<img src=\"{$user_icon->url}\" border=\"0\" alt=\"{$user_fullname}\" title=\"{$user_fullname}\" />";
157 if ($user_icon = plugin_hook("user_icon","display",$user_icon)) {
158 if ($urlonly) {
159 return $user_icon->url;
160 } else {
161 return $user_icon->html;
165 if ($urlonly) {
166 return -1;
167 } else {
168 return "<img src=\"{$CFG->wwwroot}_icon/user/-1{$extra}\" border=\"0\" alt=\"default user icon\" />";
172 // USER FLAGS //////////////////////////////////////////////////////////////////
174 // Gets the value of a flag
175 function user_flag_get($flag_name, $user_id) {
176 if ($result = get_record('user_flags','flag',$flag_name,'user_id',$user_id)) {
177 return $result->value;
179 return false;
182 // Removes a flag
183 function user_flag_unset($flag_name, $user_id) {
184 return delete_records('user_flags','flag',$flag_name,'user_id',$user_id);
187 // Adds a flag
188 function user_flag_set($flag_name, $value, $user_id) {
189 $flag_name = trim($flag_name);
190 if ($flag_name) {
191 // Unset the flag first
192 user_flag_unset($flag_name, $user_id);
194 // Then add data
195 $flag = new StdClass;
196 $flag->flag = $flag_name;
197 $flag->user_id = $user_id;
198 $flag->value = $value;
199 return insert_record('user_flags',$flag);
203 // ACCESS RESTRICTIONS /////////////////////////////////////////////////////////
205 // Get current access level
206 // Utterly deprecated (user levels no longer work like this), but kept
207 // alive for now.
208 function accesslevel($owner = -1) {
209 $currentaccess = 0;
211 // For now, there are three access levels: 0 (logged out), 1 (logged in) and 1000 (me)
212 if (logged_on == 1) {
213 $currentaccess++;
216 if ($_SESSION['userid'] == $owner) {
217 $currentaccess += 1000;
219 error_log($currentaccess);
221 return $currentaccess;
224 // Protect users to a certain access level
225 function protect($level, $owner = -1) {
226 global $CFG;
228 error_log($level);
229 error_log($owner);
230 if (accesslevel($owner) < $level) {
231 echo '<a href="' . $CFG->wwwroot . '">' . __gettext("Access Denied") . '</a>';
232 exit();
236 // NOTIFICATIONS AND MESSAGING /////////////////////////////////////////////////
241 * Send a message to a user
243 * @param integer $to the receiving user id
244 * @param integer $from the sending user id
245 * @param string $message the message body
246 * @return boolean
247 * @author Ben WerdMuller <ben@curverider.co.uk>
248 * @author Misja Hoebe <misja@curverider.co.uk>
250 function message_user($to, $from, $title, $message) {
252 global $messages, $CFG;
254 if (isset($to->ident)) {
255 $to = $to->ident;
258 $notifications = user_flag_get("emailnotifications",$to);
259 if ($notifications) {
260 $email_from = new StdClass;
261 $email_from->email = $CFG->noreplyaddress;
262 $email_from->name = $CFG->sitename;
264 if ($email_to = get_record('users', 'ident', $to))
266 if (!email_to_user($email_to,$email_from,$title,$message . "\n\n\n" . __gettext("You cannot reply to this message by email."))) {
267 $messages[] = __gettext("Failed to send email. An unknown error occurred.");
272 $m = new StdClass;
273 $m->title = $title;
274 $m->body = $message;
275 $m->from_id = $from;
276 $m->to_id = $to;
277 $m->posted = time();
278 $m->status = 'unread';
280 if (!insert_record('messages',$m)) {
281 trigger_error(__FUNCTION__.": Failed to send message from $from to $to. An unknown error occurred.", E_ERROR);
283 $messages[] = __gettext("Failed to send message. An unknown error occurred.");
284 } else {
285 plugin_hook("message", "publish", $m);
286 return true;
292 * Get user's messages
294 * Get the user's messages, optionally limit the number or the timeframe
296 * @param integer $user_id the user id
297 * @param integer $number the number of messages to retrieve
298 * @param integer $timeframe the timeframe
299 * @return mixed an ADODB RecordSet object with the results or false
300 * @author Ben WerdMuller <ben@curverider.co.uk>
302 function get_messages($user_id, $number = null, $timeframe = null) {
304 global $CFG;
306 $where = "";
307 $limit = "";
308 if ($number != null) {
309 $limit = "limit $number";
311 if ($timeframe != null) {
312 $where = " and posted > ". (time() - $timeframe);
315 return get_records_sql("select * from ".$CFG->prefix."messages where to_id = $user_id $where order by posted desc $limit");
320 * Return the basic HTML for a message (given its database row),
321 * where the title is a heading 2 and the body is in a paragraph.
323 * @param string $message the message body
324 * @return string HTML output
325 * @todo TODO refactor, separate display and logic
326 * @author Ben WerdMuller <ben@curverider.co.uk>
328 function display_message($message) {
330 global $CFG;
332 if ($message->from_id == -1) {
333 $from->name = __gettext("System");
334 } else {
335 $from = get_record_sql("select * from ".$CFG->prefix."users where ident = " . $message->from_id);
338 $title ="[" . __gettext("Message from ");
339 if ($message->from_id != -1) {
340 $title .= "<a href=\"" . $CFG->wwwroot . user_info("username",$message->from_id) . "/\">";
342 $title .= $from->name;
343 if ($message->from_id != -1) {
344 $title .= "</a>";
346 $title .= "] " . $message->title;
347 $body = "<p>" . nl2br(str_replace("\t","&nbsp;&nbsp;&nbsp;&nbsp;",activate_urls($message->body))) . "</p>";
349 $body = templates_draw(array(
350 'context' => 'databox1',
351 'name' => $title,
352 'column1' => $body
356 return $body;
361 * Send a notification to a user
363 * Could be a notifications or email,
364 * depending on a user's preferences
366 * @param integer $user_id the user id
367 * @param string $title the message title
368 * @param string $message the message body
369 * @return boolean
370 * @todo TODO fix return type
371 * @author Ben WerdMuller <ben@curverider.co.uk>
373 function notify_user($user_id, $title, $message) {
375 message_user($user_id, -1, $title, $message);
379 * Mark a user's messages as read
381 * @param integer $user_id the user's id
382 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
383 * @author Ben WerdMuller <ben@curverider.co.uk>
385 function messages_read($user_id) {
387 global $CFG;
389 $result = set_field('messages', 'status', 'read', 'to_id', $user_id);
391 return $result;
395 * Clean up user messages
397 * @global CFG global configuration
398 * @param integer $older_than
399 * @return boolean
400 * @todo TODO this should be relatively temporary (Ben?)
401 * @author Ben WerdMuller <ben@curverider.co.uk>
402 * @author Misja Hoebe <misja@curverider.co.uk>
404 function cleanup_messages($older_than) {
406 global $CFG;
408 $result = execute_sql("delete from ".$CFG->prefix."messages where posted < " . $older_than,false);
410 return $result;
413 // STATISTICS //////////////////////////////////////////////////////////////////
416 * Count number of users
418 * @global CFG global configuration
419 * @param string $type the user_type (eg 'person')
420 * @param integer $last_action the minimum last time they performed an action
421 * @return integer the number of users
422 * @author Ben WerdMuller <ben@curverider.co.uk>
424 function count_users($type = '', $last_action = 0) {
426 global $CFG;
428 $where = "1 = 1";
429 if (!empty($type)) {
430 $where .= " AND user_type = '$type'";
432 if ($last_action > 0) {
433 $where .= " AND last_action > " . $last_action;
435 if ($users = get_records_sql('SELECT user_type, count(ident) AS numusers
436 FROM '.$CFG->prefix.'users
437 WHERE '.$where.'
438 GROUP BY user_type')) {
439 if (empty($type) || sizeof($users) > 1) {
440 return $users;
442 foreach($users as $user) {
443 return $user->numusers;
447 return false;
450 // USER DEATH //////////////////////////////////////////////////////////////////
453 * Delete a user.
455 * @global CFG global configuration
456 * @param integer $user_id The unique ID of the user to delete.
457 * @return true|false Returns true if the user was deleted; false otherwise.
458 * @author Ben WerdMuller <ben@curverider.co.uk>
460 function user_delete($user_id) {
462 global $CFG;
464 // Verify that the user exists
465 if ($user = get_record_sql("select * from {$CFG->prefix}users where ident = {$user_id}")) {
467 // Call the event hook for all plugins to do their worst with the user's data
468 $user = plugin_hook("user","delete",$user);
470 // If all went well ...
471 if (!empty($user)) {
473 // Remove any icons and icon folders
474 if ($icons = get_records_sql("select * from {$CFG->prefix}icons where owner = {$user->ident}")) {
475 foreach($icons as $icon) {
476 $filepath = $CFG->dataroot . "icons/" . substr($user->username,0,1) . "/" . $user->username . "/" . $icon->filename;
477 @unlink($filepath);
479 @rmdir($filepath = $CFG->dataroot . "icons/" . substr($user->username,0,1) . "/" . $user->username . "/");
482 // Remove the user from the database!
483 delete_records("users","ident",$user->ident);
484 delete_records("user_flags","user_id",$user->ident);
485 delete_records("messages","to_id",$user->ident);
486 delete_records("messages","from_id",$user->ident);
487 return true;
491 return false;
496 * Get the user's friends
498 * @global CFG global configuration
499 * @param integer $user_id the user id
500 * @return mixed a result set packed in an array or empty
501 * @author Misja Hoebe <misja@curverider.co.uk>
503 function user_friends($user_id) {
504 global $CFG;
506 $result = get_records_sql('SELECT f.friend AS user_id,u.name FROM '.$CFG->prefix.'friends f
507 JOIN '.$CFG->prefix.'users u ON u.ident = f.friend
508 WHERE f.owner = ? ORDER BY u.name', array($user_id));
510 return $result;
514 * Get the users who have marked this user as a friend (sorted by the friends latest activity)
516 * @global CFG global configuration
517 * @param integer $user_id the user id
518 * @return mixed a result set packed in an array or empty
519 * @author Misja Hoebe <misja@curverider.co.uk>
521 function user_friends_of($user_id) {
522 global $CFG;
524 $result = get_records_sql('SELECT u.ident, u.username FROM '.$CFG->prefix.'friends f
525 JOIN '.$CFG->prefix.'users u ON u.ident = f.owner
526 WHERE friend = ? AND u.user_type = ? order by u.last_action desc', array($user_id, 'person'));
528 return $result;
532 * Add the a user to a friends list
534 * @param integer $user_id the user id
535 * @param integer $friend_id the friend id
536 * @param boolean $moderate is this a moderated friendship request
537 * @param string $type the type, used for being able to reuse functionality for communities
538 * @return boolean
539 * @author Misja Hoebe <misja@curverider.co.uk>
541 function user_friend_add($user_id, $friend_id, $moderate = false, $type = 'friendship') {
542 if (empty($user_id) || empty($friend_id)) {
543 trigger_error(__FUNCTION__.": invalid arguments (user id: $user_id, friend id: $friend_id)", E_ERROR);
546 if (record_exists('friends', 'owner', $user_id, 'friend', $friend_id)) {
547 $obj = new StdClass;
548 $obj->owner = $user_id;
549 $obj->friend = $friend_id;
551 // Type check
552 if ($type != 'friendship' || $type != 'membership') {
553 trigger_error("user_friend_add(): only type 'friendship' or 'membership' is accepted but '$type' was passed.", E_ERROR);
554 return false;
557 if ($moderation == false) {
558 // Regular friendship/membership
559 if (insert_record('friends', $obj)) {
560 plugin_hook($type, "publish", $obj);
561 return true;
562 } else {
563 return false;
565 } else {
566 // Moderated friendship/membership
567 if (insert_record('friends_requests', $obj)) {
568 plugin_hook($type, "request", $obj);
569 return true;
570 } else {
571 return false;
574 } else {
575 return false;
580 * Delete a user from a friends list
582 * @param integer $user_id the user id
583 * @param integer $friend_id the friend id
584 * @return mixed an ADODB RecordSet object with the results or false
585 * @author Misja Hoebe <misja@curverider.co.uk>
587 function user_friend_delete($user_id, $friend_id) {
588 if (empty($user_id) || empty($community_id)) {
589 trigger_error(__FUNCTION__.": invalid arguments (user id: $user_id, friend id: $friend_id)", E_ERROR);
592 $result = delete_records('friends', 'owner', $user_id, 'friend', $friend_id);
594 if ($result != false) {
595 plugin_hook("friendship", "delete", $result);
598 return $result;