Revert "MDL-42932 core_calendar: introduced calendar type system setting"
[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 stdClass $user user to create
32  * @param bool $updatepassword if true, authentication plugin will update password.
33  * @return int id of the newly created user
34  */
35 function user_create_user($user, $updatepassword = true) {
36     global $DB;
38     // Set the timecreate field to the current time.
39     if (!is_object($user)) {
40             $user = (object)$user;
41     }
43     // Check username.
44     if ($user->username !== core_text::strtolower($user->username)) {
45         throw new moodle_exception('usernamelowercase');
46     } else {
47         if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
48             throw new moodle_exception('invalidusername');
49         }
50     }
52     // Save the password in a temp value for later.
53     if ($updatepassword && isset($user->password)) {
55         // Check password toward the password policy.
56         if (!check_password_policy($user->password, $errmsg)) {
57             throw new moodle_exception($errmsg);
58         }
60         $userpassword = $user->password;
61         unset($user->password);
62     }
64     $user->timecreated = time();
65     $user->timemodified = $user->timecreated;
67     // Insert the user into the database.
68     $newuserid = $DB->insert_record('user', $user);
70     // Create USER context for this user.
71     $usercontext = context_user::instance($newuserid);
73     // Update user password if necessary.
74     if (isset($userpassword)) {
75         // Get full database user row, in case auth is default.
76         $newuser = $DB->get_record('user', array('id' => $newuserid));
77         $authplugin = get_auth_plugin($newuser->auth);
78         $authplugin->user_update_password($newuser, $userpassword);
79     }
81     // Trigger event.
82     $event = \core\event\user_created::create(
83             array(
84                 'objectid' => $newuserid,
85                 'context' => $usercontext
86                 )
87             );
88     $event->trigger();
90     return $newuserid;
91 }
93 /**
94  * Update a user with a user object (will compare against the ID)
95  *
96  * @param stdClass $user the user to update
97  * @param bool $updatepassword if true, authentication plugin will update password.
98  */
99 function user_update_user($user, $updatepassword = true) {
100     global $DB;
102     // set the timecreate field to the current time
103     if (!is_object($user)) {
104             $user = (object)$user;
105     }
107     //check username
108     if (isset($user->username)) {
109         if ($user->username !== core_text::strtolower($user->username)) {
110             throw new moodle_exception('usernamelowercase');
111         } else {
112             if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
113                 throw new moodle_exception('invalidusername');
114             }
115         }
116     }
118     // Unset password here, for updating later, if password update is required.
119     if ($updatepassword && isset($user->password)) {
121         //check password toward the password policy
122         if (!check_password_policy($user->password, $errmsg)) {
123             throw new moodle_exception($errmsg);
124         }
126         $passwd = $user->password;
127         unset($user->password);
128     }
130     $user->timemodified = time();
131     $DB->update_record('user', $user);
133     if ($updatepassword) {
134         // Get full user record.
135         $updateduser = $DB->get_record('user', array('id' => $user->id));
137         // if password was set, then update its hash
138         if (isset($passwd)) {
139             $authplugin = get_auth_plugin($updateduser->auth);
140             if ($authplugin->can_change_password()) {
141                 $authplugin->user_update_password($updateduser, $passwd);
142             }
143         }
144     }
146     // Trigger event.
147     $event = \core\event\user_updated::create(
148             array(
149                 'objectid' => $user->id,
150                 'context' => context_user::instance($user->id)
151                 )
152             );
153     $event->trigger();
156 /**
157  * Marks user deleted in internal user database and notifies the auth plugin.
158  * Also unenrols user from all roles and does other cleanup.
159  *
160  * @todo Decide if this transaction is really needed (look for internal TODO:)
161  * @param object $user Userobject before delete    (without system magic quotes)
162  * @return boolean success
163  */
164 function user_delete_user($user) {
165     return delete_user($user);
168 /**
169  * Get users by id
170  * @param array $userids id of users to retrieve
171  *
172  */
173 function user_get_users_by_id($userids) {
174     global $DB;
175     return $DB->get_records_list('user', 'id', $userids);
178 /**
179  * Returns the list of default 'displayable' fields
180  *
181  * Contains database field names but also names used to generate information, such as enrolledcourses
182  *
183  * @return array of user fields
184  */
185 function user_get_default_fields() {
186     return array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
187         'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
188         'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
189         'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
190         'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
191         'groups', 'roles', 'preferences', 'enrolledcourses'
192     );
195 /**
196  *
197  * Give user record from mdl_user, build an array conntains
198  * all user details
199  *
200  * Warning: description file urls are 'webservice/pluginfile.php' is use.
201  *          it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
202  *
203  * @param stdClass $user user record from mdl_user
204  * @param stdClass $context context object
205  * @param stdClass $course moodle course
206  * @param array $userfields required fields
207  * @return array|null
208  */
209 function user_get_user_details($user, $course = null, array $userfields = array()) {
210     global $USER, $DB, $CFG;
211     require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
212     require_once($CFG->dirroot . "/lib/filelib.php");      // file handling on description and friends
214     $defaultfields = user_get_default_fields();
216     if (empty($userfields)) {
217         $userfields = $defaultfields;
218     }
220     foreach ($userfields as $thefield) {
221         if (!in_array($thefield, $defaultfields)) {
222             throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
223         }
224     }
227     // Make sure id and fullname are included
228     if (!in_array('id', $userfields)) {
229         $userfields[] = 'id';
230     }
232     if (!in_array('fullname', $userfields)) {
233         $userfields[] = 'fullname';
234     }
236     if (!empty($course)) {
237         $context = context_course::instance($course->id);
238         $usercontext = context_user::instance($user->id);
239         $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext));
240     } else {
241         $context = context_user::instance($user->id);
242         $usercontext = $context;
243         $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
244     }
246     $currentuser = ($user->id == $USER->id);
247     $isadmin = is_siteadmin($USER);
249     $showuseridentityfields = get_extra_user_fields($context);
251     if (!empty($course)) {
252         $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
253     } else {
254         $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
255     }
256     $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
257     if (!empty($course)) {
258         $canviewuseremail = has_capability('moodle/course:useremail', $context);
259     } else {
260         $canviewuseremail = false;
261     }
262     $cannotviewdescription   = !empty($CFG->profilesforenrolledusersonly) && !$currentuser && !$DB->record_exists('role_assignments', array('userid'=>$user->id));
263     if (!empty($course)) {
264         $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
265     } else {
266         $canaccessallgroups = false;
267     }
269     if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
270         // skip this user details
271         return null;
272     }
274     $userdetails = array();
275     $userdetails['id'] = $user->id;
277     if (($isadmin or $currentuser) and in_array('username', $userfields)) {
278         $userdetails['username'] = $user->username;
279     }
280     if ($isadmin or $canviewfullnames) {
281         if (in_array('firstname', $userfields)) {
282             $userdetails['firstname'] = $user->firstname;
283         }
284         if (in_array('lastname', $userfields)) {
285             $userdetails['lastname'] = $user->lastname;
286         }
287     }
288     $userdetails['fullname'] = fullname($user);
290     if (in_array('customfields', $userfields)) {
291         $fields = $DB->get_recordset_sql("SELECT f.*
292                                             FROM {user_info_field} f
293                                             JOIN {user_info_category} c
294                                                  ON f.categoryid=c.id
295                                         ORDER BY c.sortorder ASC, f.sortorder ASC");
296         $userdetails['customfields'] = array();
297         foreach ($fields as $field) {
298             require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
299             $newfield = 'profile_field_'.$field->datatype;
300             $formfield = new $newfield($field->id, $user->id);
301             if ($formfield->is_visible() and !$formfield->is_empty()) {
302                 $userdetails['customfields'][] =
303                     array('name' => $formfield->field->name, 'value' => $formfield->data,
304                         'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
305             }
306         }
307         $fields->close();
308         // unset customfields if it's empty
309         if (empty($userdetails['customfields'])) {
310             unset($userdetails['customfields']);
311         }
312     }
314     // profile image
315     if (in_array('profileimageurl', $userfields)) {
316         $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
317         $userdetails['profileimageurl'] = $profileimageurl->out(false);
318     }
319     if (in_array('profileimageurlsmall', $userfields)) {
320         $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
321         $userdetails['profileimageurlsmall'] = $profileimageurlsmall->out(false);
322     }
324     //hidden user field
325     if ($canviewhiddenuserfields) {
326         $hiddenfields = array();
327         // address, phone1 and phone2 not appears in hidden fields list
328         // but require viewhiddenfields capability
329         // according to user/profile.php
330         if ($user->address && in_array('address', $userfields)) {
331             $userdetails['address'] = $user->address;
332         }
333     } else {
334         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
335     }
337     if ($user->phone1 && in_array('phone1', $userfields) &&
338             (in_array('phone1', $showuseridentityfields) or $canviewhiddenuserfields)) {
339         $userdetails['phone1'] = $user->phone1;
340     }
341     if ($user->phone2 && in_array('phone2', $userfields) &&
342             (in_array('phone2', $showuseridentityfields) or $canviewhiddenuserfields)) {
343         $userdetails['phone2'] = $user->phone2;
344     }
346     if (isset($user->description) &&
347         ((!isset($hiddenfields['description']) && !$cannotviewdescription) or $isadmin)) {
348         if (in_array('description', $userfields)) {
349             // Always return the descriptionformat if description is requested.
350             list($userdetails['description'], $userdetails['descriptionformat']) =
351                     external_format_text($user->description, $user->descriptionformat,
352                             $usercontext->id, 'user', 'profile', null);
353         }
354     }
356     if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
357         $userdetails['country'] = $user->country;
358     }
360     if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
361         $userdetails['city'] = $user->city;
362     }
364     if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
365         $url = $user->url;
366         if (strpos($user->url, '://') === false) {
367             $url = 'http://'. $url;
368         }
369         $user->url = clean_param($user->url, PARAM_URL);
370         $userdetails['url'] = $user->url;
371     }
373     if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
374         $userdetails['icq'] = $user->icq;
375     }
377     if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
378         $userdetails['skype'] = $user->skype;
379     }
380     if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
381         $userdetails['yahoo'] = $user->yahoo;
382     }
383     if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
384         $userdetails['aim'] = $user->aim;
385     }
386     if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
387         $userdetails['msn'] = $user->msn;
388     }
390     if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
391         if ($user->firstaccess) {
392             $userdetails['firstaccess'] = $user->firstaccess;
393         } else {
394             $userdetails['firstaccess'] = 0;
395         }
396     }
397     if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
398         if ($user->lastaccess) {
399             $userdetails['lastaccess'] = $user->lastaccess;
400         } else {
401             $userdetails['lastaccess'] = 0;
402         }
403     }
405     if (in_array('email', $userfields) && ($isadmin // The admin is allowed the users email
406       or $currentuser // Of course the current user is as well
407       or $canviewuseremail  // this is a capability in course context, it will be false in usercontext
408       or in_array('email', $showuseridentityfields)
409       or $user->maildisplay == 1
410       or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
411         $userdetails['email'] = $user->email;
412     }
414     if (in_array('interests', $userfields) && !empty($CFG->usetags)) {
415         require_once($CFG->dirroot . '/tag/lib.php');
416         if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
417             $userdetails['interests'] = $interests;
418         }
419     }
421     //Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
422     if ($isadmin or $currentuser or in_array('idnumber', $showuseridentityfields)) {
423         if (in_array('idnumber', $userfields) && $user->idnumber) {
424             $userdetails['idnumber'] = $user->idnumber;
425         }
426     }
427     if ($isadmin or $currentuser or in_array('institution', $showuseridentityfields)) {
428         if (in_array('institution', $userfields) && $user->institution) {
429             $userdetails['institution'] = $user->institution;
430         }
431     }
432     if ($isadmin or $currentuser or in_array('department', $showuseridentityfields)) {
433         if (in_array('department', $userfields) && isset($user->department)) { //isset because it's ok to have department 0
434             $userdetails['department'] = $user->department;
435         }
436     }
438     if (in_array('roles', $userfields) && !empty($course)) {
439         // not a big secret
440         $roles = get_user_roles($context, $user->id, false);
441         $userdetails['roles'] = array();
442         foreach ($roles as $role) {
443             $userdetails['roles'][] = array(
444                 'roleid'       => $role->roleid,
445                 'name'         => $role->name,
446                 'shortname'    => $role->shortname,
447                 'sortorder'    => $role->sortorder
448             );
449         }
450     }
452     // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
453     if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
454         $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid,
455                 'g.id, g.name,g.description,g.descriptionformat');
456         $userdetails['groups'] = array();
457         foreach ($usergroups as $group) {
458             list($group->description, $group->descriptionformat) =
459                 external_format_text($group->description, $group->descriptionformat,
460                         $context->id, 'group', 'description', $group->id);
461             $userdetails['groups'][] = array('id'=>$group->id, 'name'=>$group->name,
462                 'description'=>$group->description, 'descriptionformat'=>$group->descriptionformat);
463         }
464     }
465     //list of courses where the user is enrolled
466     if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
467         $enrolledcourses = array();
468         if ($mycourses = enrol_get_users_courses($user->id, true)) {
469             foreach ($mycourses as $mycourse) {
470                 if ($mycourse->category) {
471                     $coursecontext = context_course::instance($mycourse->id);
472                     $enrolledcourse = array();
473                     $enrolledcourse['id'] = $mycourse->id;
474                     $enrolledcourse['fullname'] = format_string($mycourse->fullname, true, array('context' => $coursecontext));
475                     $enrolledcourse['shortname'] = format_string($mycourse->shortname, true, array('context' => $coursecontext));
476                     $enrolledcourses[] = $enrolledcourse;
477                 }
478             }
479             $userdetails['enrolledcourses'] = $enrolledcourses;
480         }
481     }
483     //user preferences
484     if (in_array('preferences', $userfields) && $currentuser) {
485         $preferences = array();
486         $userpreferences = get_user_preferences();
487          foreach($userpreferences as $prefname => $prefvalue) {
488             $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
489          }
490          $userdetails['preferences'] = $preferences;
491     }
493     return $userdetails;
496 /**
497  * Tries to obtain user details, either recurring directly to the user's system profile
498  * or through one of the user's course enrollments (course profile).
499  *
500  * @param object $user The user.
501  * @return array if unsuccessful or the allowed user details.
502  */
503 function user_get_user_details_courses($user) {
504     global $USER;
505     $userdetails = null;
507     //  Get the courses that the user is enrolled in (only active).
508     $courses = enrol_get_users_courses($user->id, true);
510     $systemprofile = false;
511     if (can_view_user_details_cap($user) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
512         $systemprofile = true;
513     }
515     // Try using system profile.
516     if ($systemprofile) {
517         $userdetails = user_get_user_details($user, null);
518     } else {
519         // Try through course profile.
520         foreach ($courses as $course) {
521             if ($can_view_user_details_cap($user, $course) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
522                 $userdetails = user_get_user_details($user, $course);
523             }
524         }
525     }
527     return $userdetails;
530 /**
531  * Check if $USER have the necessary capabilities to obtain user details.
532  *
533  * @param object $user
534  * @param object $course if null then only consider system profile otherwise also consider the course's profile.
535  * @return bool true if $USER can view user details.
536  */
537 function can_view_user_details_cap($user, $course = null) {
538     // Check $USER has the capability to view the user details at user context.
539     $usercontext = context_user::instance($user->id);
540     $result = has_capability('moodle/user:viewdetails', $usercontext);
541     // Otherwise can $USER see them at course context.
542     if (!$result && !empty($course)) {
543         $context = context_course::instance($course->id);
544         $result = has_capability('moodle/user:viewdetails', $context);
545     }
546     return $result;
549 /**
550  * Return a list of page types
551  * @param string $pagetype current page type
552  * @param stdClass $parentcontext Block's parent context
553  * @param stdClass $currentcontext Current context of block
554  */
555 function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
556     return array('user-profile'=>get_string('page-user-profile', 'pagetype'));