weekly release 2.7dev
[moodle.git] / user / lib.php
CommitLineData
fb79269b 1<?php
2
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/>.
17
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 */
26
27
28/**
29 * Creates a user
adfb459c 30 *
bb78e249
RT
31 * @param stdClass $user user to create
32 * @param bool $updatepassword if true, authentication plugin will update password.
fb79269b 33 * @return int id of the newly created user
34 */
bb78e249 35function user_create_user($user, $updatepassword = true) {
aa8abbee 36 global $DB;
fb79269b 37
bb78e249 38 // Set the timecreate field to the current time.
fb79269b 39 if (!is_object($user)) {
6ca38802 40 $user = (object)$user;
fb79269b 41 }
bd0f26bd 42
bb78e249 43 // Check username.
2f1e464a 44 if ($user->username !== core_text::strtolower($user->username)) {
45b4464c
JM
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 }
51
bb78e249
RT
52 // Save the password in a temp value for later.
53 if ($updatepassword && isset($user->password)) {
adfb459c 54
bb78e249 55 // Check password toward the password policy.
adfb459c
JM
56 if (!check_password_policy($user->password, $errmsg)) {
57 throw new moodle_exception($errmsg);
58 }
59
60 $userpassword = $user->password;
61 unset($user->password);
62 }
bd0f26bd 63
fb79269b 64 $user->timecreated = time();
bd0f26bd 65 $user->timemodified = $user->timecreated;
fb79269b 66
bb78e249 67 // Insert the user into the database.
fb79269b 68 $newuserid = $DB->insert_record('user', $user);
69
bb78e249
RT
70 // Create USER context for this user.
71 $usercontext = context_user::instance($newuserid);
b6dcb7d9 72
bb78e249 73 // Update user password if necessary.
adfb459c 74 if (isset($userpassword)) {
bb78e249
RT
75 // Get full database user row, in case auth is default.
76 $newuser = $DB->get_record('user', array('id' => $newuserid));
adfb459c
JM
77 $authplugin = get_auth_plugin($newuser->auth);
78 $authplugin->user_update_password($newuser, $userpassword);
79 }
80
bb78e249
RT
81 // Trigger event.
82 $event = \core\event\user_created::create(
83 array(
84 'objectid' => $newuserid,
85 'context' => $usercontext
86 )
87 );
88 $event->trigger();
adfb459c 89
fb79269b 90 return $newuserid;
fb79269b 91}
92
93/**
94 * Update a user with a user object (will compare against the ID)
adfb459c 95 *
bb78e249
RT
96 * @param stdClass $user the user to update
97 * @param bool $updatepassword if true, authentication plugin will update password.
fb79269b 98 */
bb78e249 99function user_update_user($user, $updatepassword = true) {
fb79269b 100 global $DB;
bd0f26bd 101
adfb459c 102 // set the timecreate field to the current time
bd0f26bd 103 if (!is_object($user)) {
6ca38802 104 $user = (object)$user;
bd0f26bd 105 }
adfb459c 106
45b4464c
JM
107 //check username
108 if (isset($user->username)) {
2f1e464a 109 if ($user->username !== core_text::strtolower($user->username)) {
45b4464c
JM
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 }
117
bb78e249
RT
118 // Unset password here, for updating later, if password update is required.
119 if ($updatepassword && isset($user->password)) {
adfb459c
JM
120
121 //check password toward the password policy
122 if (!check_password_policy($user->password, $errmsg)) {
123 throw new moodle_exception($errmsg);
124 }
125
9e63c0ff
FS
126 $passwd = $user->password;
127 unset($user->password);
128 }
bd0f26bd 129
130 $user->timemodified = time();
fb79269b 131 $DB->update_record('user', $user);
b6dcb7d9 132
bb78e249
RT
133 if ($updatepassword) {
134 // Get full user record.
135 $updateduser = $DB->get_record('user', array('id' => $user->id));
9e63c0ff 136
bb78e249
RT
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 }
adfb459c
JM
143 }
144 }
9e63c0ff 145
bb78e249
RT
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();
adfb459c 154}
fb79269b 155
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 */
164function user_delete_user($user) {
45fb2cf8 165 return delete_user($user);
fb79269b 166}
167
168/**
169 * Get users by id
170 * @param array $userids id of users to retrieve
171 *
172 */
173function user_get_users_by_id($userids) {
174 global $DB;
175 return $DB->get_records_list('user', 'id', $userids);
176}
b1627a92 177
61c8e0d7
FM
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 */
185function 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 );
193}
01479290
DC
194
195/**
196 *
197 * Give user record from mdl_user, build an array conntains
198 * all user details
93ce0e82
JM
199 *
200 * Warning: description file urls are 'webservice/pluginfile.php' is use.
201 * it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
202 *
01479290
DC
203 * @param stdClass $user user record from mdl_user
204 * @param stdClass $context context object
205 * @param stdClass $course moodle course
ad7612f5 206 * @param array $userfields required fields
d6731600 207 * @return array|null
01479290 208 */
ad7612f5 209function user_get_user_details($user, $course = null, array $userfields = array()) {
01479290
DC
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
213
61c8e0d7 214 $defaultfields = user_get_default_fields();
ad7612f5
DC
215
216 if (empty($userfields)) {
217 $userfields = $defaultfields;
218 }
219
220 foreach ($userfields as $thefield) {
221 if (!in_array($thefield, $defaultfields)) {
222 throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
223 }
224 }
225
226
227 // Make sure id and fullname are included
228 if (!in_array('id', $userfields)) {
229 $userfields[] = 'id';
230 }
231
232 if (!in_array('fullname', $userfields)) {
233 $userfields[] = 'fullname';
234 }
235
01479290 236 if (!empty($course)) {
43731030
FM
237 $context = context_course::instance($course->id);
238 $usercontext = context_user::instance($user->id);
1e539f64 239 $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext));
01479290 240 } else {
43731030 241 $context = context_user::instance($user->id);
01479290 242 $usercontext = $context;
1e539f64 243 $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
01479290
DC
244 }
245
246 $currentuser = ($user->id == $USER->id);
247 $isadmin = is_siteadmin($USER);
248
48a7b182
JM
249 $showuseridentityfields = get_extra_user_fields($context);
250
01479290
DC
251 if (!empty($course)) {
252 $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
253 } else {
254 $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
255 }
86477112 256 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
01479290
DC
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 }
268
269 if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
270 // skip this user details
271 return null;
272 }
273
274 $userdetails = array();
275 $userdetails['id'] = $user->id;
276
ad7612f5 277 if (($isadmin or $currentuser) and in_array('username', $userfields)) {
01479290
DC
278 $userdetails['username'] = $user->username;
279 }
280 if ($isadmin or $canviewfullnames) {
ad7612f5
DC
281 if (in_array('firstname', $userfields)) {
282 $userdetails['firstname'] = $user->firstname;
283 }
284 if (in_array('lastname', $userfields)) {
285 $userdetails['lastname'] = $user->lastname;
286 }
01479290
DC
287 }
288 $userdetails['fullname'] = fullname($user);
289
ad7612f5
DC
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']);
01479290 311 }
01479290
DC
312 }
313
314 // profile image
ad7612f5
DC
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 }
01479290
DC
323
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
ad7612f5 330 if ($user->address && in_array('address', $userfields)) {
01479290
DC
331 $userdetails['address'] = $user->address;
332 }
01479290
DC
333 } else {
334 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
335 }
336
48a7b182 337 if ($user->phone1 && in_array('phone1', $userfields) &&
58f739c5 338 (in_array('phone1', $showuseridentityfields) or $canviewhiddenuserfields)) {
48a7b182
JM
339 $userdetails['phone1'] = $user->phone1;
340 }
341 if ($user->phone2 && in_array('phone2', $userfields) &&
58f739c5 342 (in_array('phone2', $showuseridentityfields) or $canviewhiddenuserfields)) {
48a7b182
JM
343 $userdetails['phone2'] = $user->phone2;
344 }
345
acf64596
JM
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);
01479290
DC
353 }
354 }
355
ad7612f5 356 if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
01479290
DC
357 $userdetails['country'] = $user->country;
358 }
359
ad7612f5 360 if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
01479290
DC
361 $userdetails['city'] = $user->city;
362 }
363
ad7612f5 364 if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
01479290
DC
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 }
372
ad7612f5 373 if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
01479290
DC
374 $userdetails['icq'] = $user->icq;
375 }
376
ad7612f5 377 if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
01479290
DC
378 $userdetails['skype'] = $user->skype;
379 }
ad7612f5 380 if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
01479290
DC
381 $userdetails['yahoo'] = $user->yahoo;
382 }
ad7612f5 383 if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
01479290
DC
384 $userdetails['aim'] = $user->aim;
385 }
ad7612f5 386 if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
01479290
DC
387 $userdetails['msn'] = $user->msn;
388 }
389
ad7612f5 390 if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
01479290
DC
391 if ($user->firstaccess) {
392 $userdetails['firstaccess'] = $user->firstaccess;
393 } else {
394 $userdetails['firstaccess'] = 0;
395 }
396 }
ad7612f5 397 if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
01479290
DC
398 if ($user->lastaccess) {
399 $userdetails['lastaccess'] = $user->lastaccess;
400 } else {
401 $userdetails['lastaccess'] = 0;
402 }
403 }
404
ca774c94
SH
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
01479290 407 or $canviewuseremail // this is a capability in course context, it will be false in usercontext
58f739c5 408 or in_array('email', $showuseridentityfields)
01479290 409 or $user->maildisplay == 1
ca774c94 410 or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
90e30b96 411 $userdetails['email'] = $user->email;
01479290
DC
412 }
413
ad7612f5 414 if (in_array('interests', $userfields) && !empty($CFG->usetags)) {
01479290
DC
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 }
420
3a3f3b22 421 //Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
58f739c5 422 if ($isadmin or $currentuser or in_array('idnumber', $showuseridentityfields)) {
48a7b182 423 if (in_array('idnumber', $userfields) && $user->idnumber) {
3a3f3b22
CW
424 $userdetails['idnumber'] = $user->idnumber;
425 }
48a7b182 426 }
58f739c5 427 if ($isadmin or $currentuser or in_array('institution', $showuseridentityfields)) {
ad7612f5 428 if (in_array('institution', $userfields) && $user->institution) {
01479290
DC
429 $userdetails['institution'] = $user->institution;
430 }
48a7b182 431 }
58f739c5 432 if ($isadmin or $currentuser or in_array('department', $showuseridentityfields)) {
ad7612f5 433 if (in_array('department', $userfields) && isset($user->department)) { //isset because it's ok to have department 0
01479290
DC
434 $userdetails['department'] = $user->department;
435 }
436 }
437
ad7612f5 438 if (in_array('roles', $userfields) && !empty($course)) {
01479290
DC
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 }
451
452 // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
ad7612f5 453 if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
93ce0e82
JM
454 $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid,
455 'g.id, g.name,g.description,g.descriptionformat');
01479290
DC
456 $userdetails['groups'] = array();
457 foreach ($usergroups as $group) {
93ce0e82
JM
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);
01479290
DC
463 }
464 }
465 //list of courses where the user is enrolled
ad7612f5 466 if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
01479290
DC
467 $enrolledcourses = array();
468 if ($mycourses = enrol_get_users_courses($user->id, true)) {
469 foreach ($mycourses as $mycourse) {
470 if ($mycourse->category) {
43731030 471 $coursecontext = context_course::instance($mycourse->id);
01479290
DC
472 $enrolledcourse = array();
473 $enrolledcourse['id'] = $mycourse->id;
43ff71a0 474 $enrolledcourse['fullname'] = format_string($mycourse->fullname, true, array('context' => $coursecontext));
8ebbb06a 475 $enrolledcourse['shortname'] = format_string($mycourse->shortname, true, array('context' => $coursecontext));
01479290
DC
476 $enrolledcourses[] = $enrolledcourse;
477 }
478 }
479 $userdetails['enrolledcourses'] = $enrolledcourses;
480 }
481 }
482
483 //user preferences
ad7612f5 484 if (in_array('preferences', $userfields) && $currentuser) {
01479290
DC
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 }
ad7612f5 492
01479290
DC
493 return $userdetails;
494}
495
86477112
FS
496/**
497 * Tries to obtain user details, either recurring directly to the user's system profile
c70b9853 498 * or through one of the user's course enrollments (course profile).
86477112 499 *
c70b9853
JM
500 * @param object $user The user.
501 * @return array if unsuccessful or the allowed user details.
86477112
FS
502 */
503function user_get_user_details_courses($user) {
504 global $USER;
505 $userdetails = null;
506
507 // Get the courses that the user is enrolled in (only active).
508 $courses = enrol_get_users_courses($user->id, true);
509
c70b9853
JM
510 $systemprofile = false;
511 if (can_view_user_details_cap($user) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
512 $systemprofile = true;
513 }
86477112 514
c70b9853 515 // Try using system profile.
86477112
FS
516 if ($systemprofile) {
517 $userdetails = user_get_user_details($user, null);
518 } else {
c70b9853 519 // Try through course profile.
86477112 520 foreach ($courses as $course) {
c70b9853 521 if ($can_view_user_details_cap($user, $course) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
86477112
FS
522 $userdetails = user_get_user_details($user, $course);
523 }
524 }
525 }
526
527 return $userdetails;
528}
529
530/**
c70b9853
JM
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.
86477112
FS
536 */
537function can_view_user_details_cap($user, $course = null) {
c70b9853 538 // Check $USER has the capability to view the user details at user context.
bea86e84 539 $usercontext = context_user::instance($user->id);
c70b9853
JM
540 $result = has_capability('moodle/user:viewdetails', $usercontext);
541 // Otherwise can $USER see them at course context.
542 if (!$result && !empty($course)) {
bea86e84 543 $context = context_course::instance($course->id);
c70b9853 544 $result = has_capability('moodle/user:viewdetails', $context);
86477112
FS
545 }
546 return $result;
547}
548
b1627a92
DC
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 */
b38e2e28 555function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
49ae1fdc 556 return array('user-profile'=>get_string('page-user-profile', 'pagetype'));
d6731600 557}