MDL-30070 message: Web Services for contact list
[moodle.git] / user / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * External user API
20  *
21  * @package    moodlecore
22  * @subpackage user
23  * @copyright  2009 Moodle Pty Ltd (http://moodle.com)
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
28 /**
29  * Creates a user
30  *
31  * @param object $user user to create
32  * @return int id of the newly created user
33  */
34 function user_create_user($user) {
35     global $DB;
37     // set the timecreate field to the current time
38     if (!is_object($user)) {
39             $user = (object)$user;
40     }
42     //check username
43     if ($user->username !== textlib::strtolower($user->username)) {
44         throw new moodle_exception('usernamelowercase');
45     } else {
46         if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
47             throw new moodle_exception('invalidusername');
48         }
49     }
51     // save the password in a temp value for later
52     if (isset($user->password)) {
54         //check password toward the password policy
55         if (!check_password_policy($user->password, $errmsg)) {
56             throw new moodle_exception($errmsg);
57         }
59         $userpassword = $user->password;
60         unset($user->password);
61     }
63     $user->timecreated = time();
64     $user->timemodified = $user->timecreated;
66     // insert the user into the database
67     $newuserid = $DB->insert_record('user', $user);
69     // trigger user_created event on the full database user row
70     $newuser = $DB->get_record('user', array('id' => $newuserid));
72     // create USER context for this user
73     context_user::instance($newuserid);
75     // update user password if necessary
76     if (isset($userpassword)) {
77         $authplugin = get_auth_plugin($newuser->auth);
78         $authplugin->user_update_password($newuser, $userpassword);
79     }
81     events_trigger('user_created', $newuser);
83     add_to_log(SITEID, 'user', get_string('create'), '/view.php?id='.$newuser->id,
84         fullname($newuser));
86     return $newuserid;
88 }
90 /**
91  * Update a user with a user object (will compare against the ID)
92  *
93  * @param object $user the user to update
94  */
95 function user_update_user($user) {
96     global $DB;
98     // set the timecreate field to the current time
99     if (!is_object($user)) {
100             $user = (object)$user;
101     }
103     //check username
104     if (isset($user->username)) {
105         if ($user->username !== textlib::strtolower($user->username)) {
106             throw new moodle_exception('usernamelowercase');
107         } else {
108             if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
109                 throw new moodle_exception('invalidusername');
110             }
111         }
112     }
114     // unset password here, for updating later
115     if (isset($user->password)) {
117         //check password toward the password policy
118         if (!check_password_policy($user->password, $errmsg)) {
119             throw new moodle_exception($errmsg);
120         }
122         $passwd = $user->password;
123         unset($user->password);
124     }
126     $user->timemodified = time();
127     $DB->update_record('user', $user);
129     // trigger user_updated event on the full database user row
130     $updateduser = $DB->get_record('user', array('id' => $user->id));
132     // if password was set, then update its hash
133     if (isset($passwd)) {
134         $authplugin = get_auth_plugin($updateduser->auth);
135         if ($authplugin->can_change_password()) {
136             $authplugin->user_update_password($updateduser, $passwd);
137         }
138     }
140     events_trigger('user_updated', $updateduser);
142     add_to_log(SITEID, 'user', get_string('update'), '/view.php?id='.$updateduser->id,
143         fullname($updateduser));
147 /**
148  * Marks user deleted in internal user database and notifies the auth plugin.
149  * Also unenrols user from all roles and does other cleanup.
150  *
151  * @todo Decide if this transaction is really needed (look for internal TODO:)
152  * @param object $user Userobject before delete    (without system magic quotes)
153  * @return boolean success
154  */
155 function user_delete_user($user) {
156     return delete_user($user);
159 /**
160  * Get users by id
161  * @param array $userids id of users to retrieve
162  *
163  */
164 function user_get_users_by_id($userids) {
165     global $DB;
166     return $DB->get_records_list('user', 'id', $userids);
169 /**
170  * Returns the list of default 'displayable' fields
171  *
172  * Contains database field names but also names used to generate information, such as enrolledcourses
173  *
174  * @return array of user fields
175  */
176 function user_get_default_fields() {
177     return array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
178         'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
179         'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
180         'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
181         'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
182         'groups', 'roles', 'preferences', 'enrolledcourses'
183     );
186 /**
187  *
188  * Give user record from mdl_user, build an array conntains
189  * all user details
190  *
191  * Warning: description file urls are 'webservice/pluginfile.php' is use.
192  *          it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
193  *
194  * @param stdClass $user user record from mdl_user
195  * @param stdClass $context context object
196  * @param stdClass $course moodle course
197  * @param array $userfields required fields
198  * @return array|null
199  */
200 function user_get_user_details($user, $course = null, array $userfields = array()) {
201     global $USER, $DB, $CFG;
202     require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
203     require_once($CFG->dirroot . "/lib/filelib.php");      // file handling on description and friends
205     $defaultfields = user_get_default_fields();
207     if (empty($userfields)) {
208         $userfields = $defaultfields;
209     }
211     foreach ($userfields as $thefield) {
212         if (!in_array($thefield, $defaultfields)) {
213             throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
214         }
215     }
218     // Make sure id and fullname are included
219     if (!in_array('id', $userfields)) {
220         $userfields[] = 'id';
221     }
223     if (!in_array('fullname', $userfields)) {
224         $userfields[] = 'fullname';
225     }
227     if (!empty($course)) {
228         $context = context_course::instance($course->id);
229         $usercontext = context_user::instance($user->id);
230         $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext));
231     } else {
232         $context = context_user::instance($user->id);
233         $usercontext = $context;
234         $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
235     }
237     $currentuser = ($user->id == $USER->id);
238     $isadmin = is_siteadmin($USER);
240     $showuseridentityfields = get_extra_user_fields($context);
242     if (!empty($course)) {
243         $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
244     } else {
245         $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
246     }
247     $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
248     if (!empty($course)) {
249         $canviewuseremail = has_capability('moodle/course:useremail', $context);
250     } else {
251         $canviewuseremail = false;
252     }
253     $cannotviewdescription   = !empty($CFG->profilesforenrolledusersonly) && !$currentuser && !$DB->record_exists('role_assignments', array('userid'=>$user->id));
254     if (!empty($course)) {
255         $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
256     } else {
257         $canaccessallgroups = false;
258     }
260     if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
261         // skip this user details
262         return null;
263     }
265     $userdetails = array();
266     $userdetails['id'] = $user->id;
268     if (($isadmin or $currentuser) and in_array('username', $userfields)) {
269         $userdetails['username'] = $user->username;
270     }
271     if ($isadmin or $canviewfullnames) {
272         if (in_array('firstname', $userfields)) {
273             $userdetails['firstname'] = $user->firstname;
274         }
275         if (in_array('lastname', $userfields)) {
276             $userdetails['lastname'] = $user->lastname;
277         }
278     }
279     $userdetails['fullname'] = fullname($user);
281     if (in_array('customfields', $userfields)) {
282         $fields = $DB->get_recordset_sql("SELECT f.*
283                                             FROM {user_info_field} f
284                                             JOIN {user_info_category} c
285                                                  ON f.categoryid=c.id
286                                         ORDER BY c.sortorder ASC, f.sortorder ASC");
287         $userdetails['customfields'] = array();
288         foreach ($fields as $field) {
289             require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
290             $newfield = 'profile_field_'.$field->datatype;
291             $formfield = new $newfield($field->id, $user->id);
292             if ($formfield->is_visible() and !$formfield->is_empty()) {
293                 $userdetails['customfields'][] =
294                     array('name' => $formfield->field->name, 'value' => $formfield->data,
295                         'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
296             }
297         }
298         $fields->close();
299         // unset customfields if it's empty
300         if (empty($userdetails['customfields'])) {
301             unset($userdetails['customfields']);
302         }
303     }
305     // profile image
306     if (in_array('profileimageurl', $userfields)) {
307         $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
308         $userdetails['profileimageurl'] = $profileimageurl->out(false);
309     }
310     if (in_array('profileimageurlsmall', $userfields)) {
311         $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
312         $userdetails['profileimageurlsmall'] = $profileimageurlsmall->out(false);
313     }
315     //hidden user field
316     if ($canviewhiddenuserfields) {
317         $hiddenfields = array();
318         // address, phone1 and phone2 not appears in hidden fields list
319         // but require viewhiddenfields capability
320         // according to user/profile.php
321         if ($user->address && in_array('address', $userfields)) {
322             $userdetails['address'] = $user->address;
323         }
324     } else {
325         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
326     }
328     if ($user->phone1 && in_array('phone1', $userfields) &&
329             (isset($showuseridentityfields['phone1']) or $canviewhiddenuserfields)) {
330         $userdetails['phone1'] = $user->phone1;
331     }
332     if ($user->phone2 && in_array('phone2', $userfields) &&
333             (isset($showuseridentityfields['phone2']) or $canviewhiddenuserfields)) {
334         $userdetails['phone2'] = $user->phone2;
335     }
337     if (isset($user->description) &&
338         ((!isset($hiddenfields['description']) && !$cannotviewdescription) or $isadmin)) {
339         if (in_array('description', $userfields)) {
340             // Always return the descriptionformat if description is requested.
341             list($userdetails['description'], $userdetails['descriptionformat']) =
342                     external_format_text($user->description, $user->descriptionformat,
343                             $usercontext->id, 'user', 'profile', null);
344         }
345     }
347     if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
348         $userdetails['country'] = $user->country;
349     }
351     if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
352         $userdetails['city'] = $user->city;
353     }
355     if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
356         $url = $user->url;
357         if (strpos($user->url, '://') === false) {
358             $url = 'http://'. $url;
359         }
360         $user->url = clean_param($user->url, PARAM_URL);
361         $userdetails['url'] = $user->url;
362     }
364     if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
365         $userdetails['icq'] = $user->icq;
366     }
368     if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
369         $userdetails['skype'] = $user->skype;
370     }
371     if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
372         $userdetails['yahoo'] = $user->yahoo;
373     }
374     if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
375         $userdetails['aim'] = $user->aim;
376     }
377     if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
378         $userdetails['msn'] = $user->msn;
379     }
381     if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
382         if ($user->firstaccess) {
383             $userdetails['firstaccess'] = $user->firstaccess;
384         } else {
385             $userdetails['firstaccess'] = 0;
386         }
387     }
388     if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
389         if ($user->lastaccess) {
390             $userdetails['lastaccess'] = $user->lastaccess;
391         } else {
392             $userdetails['lastaccess'] = 0;
393         }
394     }
396     if (in_array('email', $userfields) && ($isadmin // The admin is allowed the users email
397       or $currentuser // Of course the current user is as well
398       or $canviewuseremail  // this is a capability in course context, it will be false in usercontext
399       or isset($showuseridentityfields['email'])
400       or $user->maildisplay == 1
401       or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
402         $userdetails['email'] = $user->email;
403     }
405     if (in_array('interests', $userfields) && !empty($CFG->usetags)) {
406         require_once($CFG->dirroot . '/tag/lib.php');
407         if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
408             $userdetails['interests'] = $interests;
409         }
410     }
412     //Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
413     if ($isadmin or $currentuser or isset($showuseridentityfields['idnumber'])) {
414         if (in_array('idnumber', $userfields) && $user->idnumber) {
415             $userdetails['idnumber'] = $user->idnumber;
416         }
417     }
418     if ($isadmin or $currentuser or isset($showuseridentityfields['institution'])) {
419         if (in_array('institution', $userfields) && $user->institution) {
420             $userdetails['institution'] = $user->institution;
421         }
422     }
423     if ($isadmin or $currentuser or isset($showuseridentityfields['department'])) {
424         if (in_array('department', $userfields) && isset($user->department)) { //isset because it's ok to have department 0
425             $userdetails['department'] = $user->department;
426         }
427     }
429     if (in_array('roles', $userfields) && !empty($course)) {
430         // not a big secret
431         $roles = get_user_roles($context, $user->id, false);
432         $userdetails['roles'] = array();
433         foreach ($roles as $role) {
434             $userdetails['roles'][] = array(
435                 'roleid'       => $role->roleid,
436                 'name'         => $role->name,
437                 'shortname'    => $role->shortname,
438                 'sortorder'    => $role->sortorder
439             );
440         }
441     }
443     // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
444     if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
445         $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid,
446                 'g.id, g.name,g.description,g.descriptionformat');
447         $userdetails['groups'] = array();
448         foreach ($usergroups as $group) {
449             list($group->description, $group->descriptionformat) =
450                 external_format_text($group->description, $group->descriptionformat,
451                         $context->id, 'group', 'description', $group->id);
452             $userdetails['groups'][] = array('id'=>$group->id, 'name'=>$group->name,
453                 'description'=>$group->description, 'descriptionformat'=>$group->descriptionformat);
454         }
455     }
456     //list of courses where the user is enrolled
457     if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
458         $enrolledcourses = array();
459         if ($mycourses = enrol_get_users_courses($user->id, true)) {
460             foreach ($mycourses as $mycourse) {
461                 if ($mycourse->category) {
462                     $coursecontext = context_course::instance($mycourse->id);
463                     $enrolledcourse = array();
464                     $enrolledcourse['id'] = $mycourse->id;
465                     $enrolledcourse['fullname'] = format_string($mycourse->fullname, true, array('context' => $coursecontext));
466                     $enrolledcourse['shortname'] = format_string($mycourse->shortname, true, array('context' => $coursecontext));
467                     $enrolledcourses[] = $enrolledcourse;
468                 }
469             }
470             $userdetails['enrolledcourses'] = $enrolledcourses;
471         }
472     }
474     //user preferences
475     if (in_array('preferences', $userfields) && $currentuser) {
476         $preferences = array();
477         $userpreferences = get_user_preferences();
478          foreach($userpreferences as $prefname => $prefvalue) {
479             $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
480          }
481          $userdetails['preferences'] = $preferences;
482     }
484     return $userdetails;
487 /**
488  * Tries to obtain user details, either recurring directly to the user's system profile
489  * or through one of the user's course enrollments (course profile).
490  *
491  * @param object $user The user.
492  * @return array if unsuccessful or the allowed user details.
493  */
494 function user_get_user_details_courses($user) {
495     global $USER;
496     $userdetails = null;
498     //  Get the courses that the user is enrolled in (only active).
499     $courses = enrol_get_users_courses($user->id, true);
501     $systemprofile = false;
502     if (can_view_user_details_cap($user) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
503         $systemprofile = true;
504     }
506     // Try using system profile.
507     if ($systemprofile) {
508         $userdetails = user_get_user_details($user, null);
509     } else {
510         // Try through course profile.
511         foreach ($courses as $course) {
512             if ($can_view_user_details_cap($user, $course) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
513                 $userdetails = user_get_user_details($user, $course);
514             }
515         }
516     }
518     return $userdetails;
521 /**
522  * Check if $USER have the necessary capabilities to obtain user details.
523  *
524  * @param object $user
525  * @param object $course if null then only consider system profile otherwise also consider the course's profile.
526  * @return bool true if $USER can view user details.
527  */
528 function can_view_user_details_cap($user, $course = null) {
529     // Check $USER has the capability to view the user details at user context.
530     $usercontext = get_context_instance(CONTEXT_USER, $user->id);
531     $result = has_capability('moodle/user:viewdetails', $usercontext);
532     // Otherwise can $USER see them at course context.
533     if (!$result && !empty($course)) {
534         $context = get_context_instance(CONTEXT_COURSE, $course->id);
535         $result = has_capability('moodle/user:viewdetails', $context);
536     }
537     return $result;
540 /**
541  * Return a list of page types
542  * @param string $pagetype current page type
543  * @param stdClass $parentcontext Block's parent context
544  * @param stdClass $currentcontext Current context of block
545  */
546 function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
547     return array('user-profile'=>get_string('page-user-profile', 'pagetype'));