2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
21 * @copyright 2009 Moodle Pty Ltd (http://moodle.com)
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 define('USER_FILTER_ENROLMENT', 1);
26 define('USER_FILTER_GROUP', 2);
27 define('USER_FILTER_LAST_ACCESS', 3);
28 define('USER_FILTER_ROLE', 4);
29 define('USER_FILTER_STATUS', 5);
30 define('USER_FILTER_STRING', 6);
35 * @throws moodle_exception
36 * @param stdClass $user user to create
37 * @param bool $updatepassword if true, authentication plugin will update password.
38 * @param bool $triggerevent set false if user_created event should not be triggred.
39 * This will not affect user_password_updated event triggering.
40 * @return int id of the newly created user
42 function user_create_user($user, $updatepassword = true, $triggerevent = true) {
45 // Set the timecreate field to the current time.
46 if (!is_object($user)) {
47 $user = (object) $user;
51 if (trim($user->username
) === '') {
52 throw new moodle_exception('invalidusernameblank');
55 if ($user->username
!== core_text
::strtolower($user->username
)) {
56 throw new moodle_exception('usernamelowercase');
59 if ($user->username
!== core_user
::clean_field($user->username
, 'username')) {
60 throw new moodle_exception('invalidusername');
63 // Save the password in a temp value for later.
64 if ($updatepassword && isset($user->password
)) {
66 // Check password toward the password policy.
67 if (!check_password_policy($user->password
, $errmsg)) {
68 throw new moodle_exception($errmsg);
71 $userpassword = $user->password
;
72 unset($user->password
);
75 // Apply default values for user preferences that are stored in users table.
76 if (!isset($user->calendartype
)) {
77 $user->calendartype
= core_user
::get_property_default('calendartype');
79 if (!isset($user->maildisplay
)) {
80 $user->maildisplay
= core_user
::get_property_default('maildisplay');
82 if (!isset($user->mailformat
)) {
83 $user->mailformat
= core_user
::get_property_default('mailformat');
85 if (!isset($user->maildigest
)) {
86 $user->maildigest
= core_user
::get_property_default('maildigest');
88 if (!isset($user->autosubscribe
)) {
89 $user->autosubscribe
= core_user
::get_property_default('autosubscribe');
91 if (!isset($user->trackforums
)) {
92 $user->trackforums
= core_user
::get_property_default('trackforums');
94 if (!isset($user->lang
)) {
95 $user->lang
= core_user
::get_property_default('lang');
98 $user->timecreated
= time();
99 $user->timemodified
= $user->timecreated
;
101 // Validate user data object.
102 $uservalidation = core_user
::validate($user);
103 if ($uservalidation !== true) {
104 foreach ($uservalidation as $field => $message) {
105 debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER
);
106 $user->$field = core_user
::clean_field($user->$field, $field);
110 // Insert the user into the database.
111 $newuserid = $DB->insert_record('user', $user);
113 // Create USER context for this user.
114 $usercontext = context_user
::instance($newuserid);
116 // Update user password if necessary.
117 if (isset($userpassword)) {
118 // Get full database user row, in case auth is default.
119 $newuser = $DB->get_record('user', array('id' => $newuserid));
120 $authplugin = get_auth_plugin($newuser->auth
);
121 $authplugin->user_update_password($newuser, $userpassword);
124 // Trigger event If required.
126 \core\event\user_created
::create_from_userid($newuserid)->trigger();
129 // Purge the associated caches.
130 cache_helper
::purge_by_event('createduser');
136 * Update a user with a user object (will compare against the ID)
138 * @throws moodle_exception
139 * @param stdClass $user the user to update
140 * @param bool $updatepassword if true, authentication plugin will update password.
141 * @param bool $triggerevent set false if user_updated event should not be triggred.
142 * This will not affect user_password_updated event triggering.
144 function user_update_user($user, $updatepassword = true, $triggerevent = true) {
147 // Set the timecreate field to the current time.
148 if (!is_object($user)) {
149 $user = (object) $user;
153 if (isset($user->username
)) {
154 if ($user->username
!== core_text
::strtolower($user->username
)) {
155 throw new moodle_exception('usernamelowercase');
157 if ($user->username
!== core_user
::clean_field($user->username
, 'username')) {
158 throw new moodle_exception('invalidusername');
163 // Unset password here, for updating later, if password update is required.
164 if ($updatepassword && isset($user->password
)) {
166 // Check password toward the password policy.
167 if (!check_password_policy($user->password
, $errmsg)) {
168 throw new moodle_exception($errmsg);
171 $passwd = $user->password
;
172 unset($user->password
);
175 // Make sure calendartype, if set, is valid.
176 if (empty($user->calendartype
)) {
177 // Unset this variable, must be an empty string, which we do not want to update the calendartype to.
178 unset($user->calendartype
);
181 $user->timemodified
= time();
183 // Validate user data object.
184 $uservalidation = core_user
::validate($user);
185 if ($uservalidation !== true) {
186 foreach ($uservalidation as $field => $message) {
187 debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER
);
188 $user->$field = core_user
::clean_field($user->$field, $field);
192 $DB->update_record('user', $user);
194 if ($updatepassword) {
195 // Get full user record.
196 $updateduser = $DB->get_record('user', array('id' => $user->id
));
198 // If password was set, then update its hash.
199 if (isset($passwd)) {
200 $authplugin = get_auth_plugin($updateduser->auth
);
201 if ($authplugin->can_change_password()) {
202 $authplugin->user_update_password($updateduser, $passwd);
206 // Trigger event if required.
208 \core\event\user_updated
::create_from_userid($user->id
)->trigger();
213 * Marks user deleted in internal user database and notifies the auth plugin.
214 * Also unenrols user from all roles and does other cleanup.
216 * @todo Decide if this transaction is really needed (look for internal TODO:)
217 * @param object $user Userobject before delete (without system magic quotes)
218 * @return boolean success
220 function user_delete_user($user) {
221 return delete_user($user);
227 * @param array $userids id of users to retrieve
230 function user_get_users_by_id($userids) {
232 return $DB->get_records_list('user', 'id', $userids);
236 * Returns the list of default 'displayable' fields
238 * Contains database field names but also names used to generate information, such as enrolledcourses
240 * @return array of user fields
242 function user_get_default_fields() {
243 return array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
244 'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
245 'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
246 'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
247 'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
248 'groups', 'roles', 'preferences', 'enrolledcourses', 'suspended'
254 * Give user record from mdl_user, build an array contains all user details.
256 * Warning: description file urls are 'webservice/pluginfile.php' is use.
257 * it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
259 * @throws moodle_exception
260 * @param stdClass $user user record from mdl_user
261 * @param stdClass $course moodle course
262 * @param array $userfields required fields
265 function user_get_user_details($user, $course = null, array $userfields = array()) {
266 global $USER, $DB, $CFG, $PAGE;
267 require_once($CFG->dirroot
. "/user/profile/lib.php"); // Custom field library.
268 require_once($CFG->dirroot
. "/lib/filelib.php"); // File handling on description and friends.
270 $defaultfields = user_get_default_fields();
272 if (empty($userfields)) {
273 $userfields = $defaultfields;
276 foreach ($userfields as $thefield) {
277 if (!in_array($thefield, $defaultfields)) {
278 throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
282 // Make sure id and fullname are included.
283 if (!in_array('id', $userfields)) {
284 $userfields[] = 'id';
287 if (!in_array('fullname', $userfields)) {
288 $userfields[] = 'fullname';
291 if (!empty($course)) {
292 $context = context_course
::instance($course->id
);
293 $usercontext = context_user
::instance($user->id
);
294 $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) ||
has_capability('moodle/user:viewdetails', $usercontext));
296 $context = context_user
::instance($user->id
);
297 $usercontext = $context;
298 $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
301 $currentuser = ($user->id
== $USER->id
);
302 $isadmin = is_siteadmin($USER);
304 $showuseridentityfields = get_extra_user_fields($context);
306 if (!empty($course)) {
307 $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
309 $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
311 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
312 if (!empty($course)) {
313 $canviewuseremail = has_capability('moodle/course:useremail', $context);
315 $canviewuseremail = false;
317 $cannotviewdescription = !empty($CFG->profilesforenrolledusersonly
) && !$currentuser && !$DB->record_exists('role_assignments', array('userid' => $user->id
));
318 if (!empty($course)) {
319 $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
321 $canaccessallgroups = false;
324 if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id
)) {
325 // Skip this user details.
329 $userdetails = array();
330 $userdetails['id'] = $user->id
;
332 if (in_array('username', $userfields)) {
333 if ($currentuser or has_capability('moodle/user:viewalldetails', $context)) {
334 $userdetails['username'] = $user->username
;
337 if ($isadmin or $canviewfullnames) {
338 if (in_array('firstname', $userfields)) {
339 $userdetails['firstname'] = $user->firstname
;
341 if (in_array('lastname', $userfields)) {
342 $userdetails['lastname'] = $user->lastname
;
345 $userdetails['fullname'] = fullname($user, $canviewfullnames);
347 if (in_array('customfields', $userfields)) {
348 $categories = profile_get_user_fields_with_data_by_category($user->id
);
349 $userdetails['customfields'] = array();
350 foreach ($categories as $categoryid => $fields) {
351 foreach ($fields as $formfield) {
352 if ($formfield->is_visible() and !$formfield->is_empty()) {
354 // TODO: Part of MDL-50728, this conditional coding must be moved to
355 // proper profile fields API so they are self-contained.
356 // We only use display_data in fields that require text formatting.
357 if ($formfield->field
->datatype
== 'text' or $formfield->field
->datatype
== 'textarea') {
358 $fieldvalue = $formfield->display_data();
360 // Cases: datetime, checkbox and menu.
361 $fieldvalue = $formfield->data
;
364 $userdetails['customfields'][] =
365 array('name' => $formfield->field
->name
, 'value' => $fieldvalue,
366 'type' => $formfield->field
->datatype
, 'shortname' => $formfield->field
->shortname
);
370 // Unset customfields if it's empty.
371 if (empty($userdetails['customfields'])) {
372 unset($userdetails['customfields']);
377 if (in_array('profileimageurl', $userfields)) {
378 $userpicture = new user_picture($user);
379 $userpicture->size
= 1; // Size f1.
380 $userdetails['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
382 if (in_array('profileimageurlsmall', $userfields)) {
383 if (!isset($userpicture)) {
384 $userpicture = new user_picture($user);
386 $userpicture->size
= 0; // Size f2.
387 $userdetails['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
390 // Hidden user field.
391 if ($canviewhiddenuserfields) {
392 $hiddenfields = array();
393 // Address, phone1 and phone2 not appears in hidden fields list but require viewhiddenfields capability
394 // according to user/profile.php.
395 if (!empty($user->address
) && in_array('address', $userfields)) {
396 $userdetails['address'] = $user->address
;
399 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields
));
402 if (!empty($user->phone1
) && in_array('phone1', $userfields) &&
403 (in_array('phone1', $showuseridentityfields) or $canviewhiddenuserfields)) {
404 $userdetails['phone1'] = $user->phone1
;
406 if (!empty($user->phone2
) && in_array('phone2', $userfields) &&
407 (in_array('phone2', $showuseridentityfields) or $canviewhiddenuserfields)) {
408 $userdetails['phone2'] = $user->phone2
;
411 if (isset($user->description
) &&
412 ((!isset($hiddenfields['description']) && !$cannotviewdescription) or $isadmin)) {
413 if (in_array('description', $userfields)) {
414 // Always return the descriptionformat if description is requested.
415 list($userdetails['description'], $userdetails['descriptionformat']) =
416 external_format_text($user->description
, $user->descriptionformat
,
417 $usercontext->id
, 'user', 'profile', null);
421 if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country
) {
422 $userdetails['country'] = $user->country
;
425 if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city
) {
426 $userdetails['city'] = $user->city
;
429 if (in_array('url', $userfields) && $user->url
&& (!isset($hiddenfields['webpage']) or $isadmin)) {
431 if (strpos($user->url
, '://') === false) {
432 $url = 'http://'. $url;
434 $user->url
= clean_param($user->url
, PARAM_URL
);
435 $userdetails['url'] = $user->url
;
438 if (in_array('icq', $userfields) && $user->icq
&& (!isset($hiddenfields['icqnumber']) or $isadmin)) {
439 $userdetails['icq'] = $user->icq
;
442 if (in_array('skype', $userfields) && $user->skype
&& (!isset($hiddenfields['skypeid']) or $isadmin)) {
443 $userdetails['skype'] = $user->skype
;
445 if (in_array('yahoo', $userfields) && $user->yahoo
&& (!isset($hiddenfields['yahooid']) or $isadmin)) {
446 $userdetails['yahoo'] = $user->yahoo
;
448 if (in_array('aim', $userfields) && $user->aim
&& (!isset($hiddenfields['aimid']) or $isadmin)) {
449 $userdetails['aim'] = $user->aim
;
451 if (in_array('msn', $userfields) && $user->msn
&& (!isset($hiddenfields['msnid']) or $isadmin)) {
452 $userdetails['msn'] = $user->msn
;
454 if (in_array('suspended', $userfields) && (!isset($hiddenfields['suspended']) or $isadmin)) {
455 $userdetails['suspended'] = (bool)$user->suspended
;
458 if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
459 if ($user->firstaccess
) {
460 $userdetails['firstaccess'] = $user->firstaccess
;
462 $userdetails['firstaccess'] = 0;
465 if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
466 if ($user->lastaccess
) {
467 $userdetails['lastaccess'] = $user->lastaccess
;
469 $userdetails['lastaccess'] = 0;
473 if (in_array('email', $userfields) && ($isadmin // The admin is allowed the users email.
474 or $currentuser // Of course the current user is as well.
475 or $canviewuseremail // This is a capability in course context, it will be false in usercontext.
476 or in_array('email', $showuseridentityfields)
477 or $user->maildisplay
== 1
478 or ($user->maildisplay
== 2 and enrol_sharing_course($user, $USER)))) {
479 $userdetails['email'] = $user->email
;
482 if (in_array('interests', $userfields)) {
483 $interests = core_tag_tag
::get_item_tags_array('core', 'user', $user->id
, core_tag_tag
::BOTH_STANDARD_AND_NOT
, 0, false);
485 $userdetails['interests'] = join(', ', $interests);
489 // Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
490 if (in_array('idnumber', $userfields) && $user->idnumber
) {
491 if (in_array('idnumber', $showuseridentityfields) or $currentuser or
492 has_capability('moodle/user:viewalldetails', $context)) {
493 $userdetails['idnumber'] = $user->idnumber
;
496 if (in_array('institution', $userfields) && $user->institution
) {
497 if (in_array('institution', $showuseridentityfields) or $currentuser or
498 has_capability('moodle/user:viewalldetails', $context)) {
499 $userdetails['institution'] = $user->institution
;
502 // Isset because it's ok to have department 0.
503 if (in_array('department', $userfields) && isset($user->department
)) {
504 if (in_array('department', $showuseridentityfields) or $currentuser or
505 has_capability('moodle/user:viewalldetails', $context)) {
506 $userdetails['department'] = $user->department
;
510 if (in_array('roles', $userfields) && !empty($course)) {
512 $roles = get_user_roles($context, $user->id
, false);
513 $userdetails['roles'] = array();
514 foreach ($roles as $role) {
515 $userdetails['roles'][] = array(
516 'roleid' => $role->roleid
,
517 'name' => $role->name
,
518 'shortname' => $role->shortname
,
519 'sortorder' => $role->sortorder
524 // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group.
525 if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
526 $usergroups = groups_get_all_groups($course->id
, $user->id
, $course->defaultgroupingid
,
527 'g.id, g.name,g.description,g.descriptionformat');
528 $userdetails['groups'] = array();
529 foreach ($usergroups as $group) {
530 list($group->description
, $group->descriptionformat
) =
531 external_format_text($group->description
, $group->descriptionformat
,
532 $context->id
, 'group', 'description', $group->id
);
533 $userdetails['groups'][] = array('id' => $group->id
, 'name' => $group->name
,
534 'description' => $group->description
, 'descriptionformat' => $group->descriptionformat
);
537 // List of courses where the user is enrolled.
538 if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
539 $enrolledcourses = array();
540 if ($mycourses = enrol_get_users_courses($user->id
, true)) {
541 foreach ($mycourses as $mycourse) {
542 if ($mycourse->category
) {
543 $coursecontext = context_course
::instance($mycourse->id
);
544 $enrolledcourse = array();
545 $enrolledcourse['id'] = $mycourse->id
;
546 $enrolledcourse['fullname'] = format_string($mycourse->fullname
, true, array('context' => $coursecontext));
547 $enrolledcourse['shortname'] = format_string($mycourse->shortname
, true, array('context' => $coursecontext));
548 $enrolledcourses[] = $enrolledcourse;
551 $userdetails['enrolledcourses'] = $enrolledcourses;
556 if (in_array('preferences', $userfields) && $currentuser) {
557 $preferences = array();
558 $userpreferences = get_user_preferences();
559 foreach ($userpreferences as $prefname => $prefvalue) {
560 $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
562 $userdetails['preferences'] = $preferences;
565 if ($currentuser or has_capability('moodle/user:viewalldetails', $context)) {
566 $extrafields = ['auth', 'confirmed', 'lang', 'theme', 'timezone', 'mailformat'];
567 foreach ($extrafields as $extrafield) {
568 if (in_array($extrafield, $userfields) && isset($user->$extrafield)) {
569 $userdetails[$extrafield] = $user->$extrafield;
574 // Clean lang and auth fields for external functions (it may content uninstalled themes or language packs).
575 if (isset($userdetails['lang'])) {
576 $userdetails['lang'] = clean_param($userdetails['lang'], PARAM_LANG
);
578 if (isset($userdetails['theme'])) {
579 $userdetails['theme'] = clean_param($userdetails['theme'], PARAM_THEME
);
586 * Tries to obtain user details, either recurring directly to the user's system profile
587 * or through one of the user's course enrollments (course profile).
589 * @param stdClass $user The user.
590 * @return array if unsuccessful or the allowed user details.
592 function user_get_user_details_courses($user) {
596 // Get the courses that the user is enrolled in (only active).
597 $courses = enrol_get_users_courses($user->id
, true);
599 $systemprofile = false;
600 if (can_view_user_details_cap($user) ||
($user->id
== $USER->id
) ||
has_coursecontact_role($user->id
)) {
601 $systemprofile = true;
604 // Try using system profile.
605 if ($systemprofile) {
606 $userdetails = user_get_user_details($user, null);
608 // Try through course profile.
609 foreach ($courses as $course) {
610 if (can_view_user_details_cap($user, $course) ||
($user->id
== $USER->id
) ||
has_coursecontact_role($user->id
)) {
611 $userdetails = user_get_user_details($user, $course);
620 * Check if $USER have the necessary capabilities to obtain user details.
622 * @param stdClass $user
623 * @param stdClass $course if null then only consider system profile otherwise also consider the course's profile.
624 * @return bool true if $USER can view user details.
626 function can_view_user_details_cap($user, $course = null) {
627 // Check $USER has the capability to view the user details at user context.
628 $usercontext = context_user
::instance($user->id
);
629 $result = has_capability('moodle/user:viewdetails', $usercontext);
630 // Otherwise can $USER see them at course context.
631 if (!$result && !empty($course)) {
632 $context = context_course
::instance($course->id
);
633 $result = has_capability('moodle/user:viewdetails', $context);
639 * Return a list of page types
640 * @param string $pagetype current page type
641 * @param stdClass $parentcontext Block's parent context
642 * @param stdClass $currentcontext Current context of block
645 function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
646 return array('user-profile' => get_string('page-user-profile', 'pagetype'));
650 * Count the number of failed login attempts for the given user, since last successful login.
652 * @param int|stdclass $user user id or object.
653 * @param bool $reset Resets failed login count, if set to true.
655 * @return int number of failed login attempts since the last successful login.
657 function user_count_login_failures($user, $reset = true) {
660 if (!is_object($user)) {
661 $user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST
);
663 if ($user->deleted
) {
664 // Deleted user, nothing to do.
667 $count = get_user_preferences('login_failed_count_since_success', 0, $user);
669 set_user_preference('login_failed_count_since_success', 0, $user);
675 * Converts a string into a flat array of menu items, where each menu items is a
676 * stdClass with fields type, url, title, pix, and imgsrc.
678 * @param string $text the menu items definition
679 * @param moodle_page $page the current page
682 function user_convert_text_to_menu_items($text, $page) {
683 global $OUTPUT, $CFG;
685 $lines = explode("\n", $text);
691 foreach ($lines as $line) {
693 $bits = explode('|', $line, 3);
695 if (preg_match("/^#+$/", $line)) {
696 $itemtype = 'divider';
697 } else if (!array_key_exists(0, $bits) or empty($bits[0])) {
698 // Every item must have a name to be valid.
701 $bits[0] = ltrim($bits[0], '-');
705 $child = new stdClass();
706 $child->itemtype
= $itemtype;
707 if ($itemtype === 'divider') {
708 // Add the divider to the list of children and skip link
710 $children[] = $child;
715 $namebits = explode(',', $bits[0], 2);
716 if (count($namebits) == 2) {
717 // Check the validity of the identifier part of the string.
718 if (clean_param($namebits[0], PARAM_STRINGID
) !== '') {
719 // Treat this as a language string.
720 $child->title
= get_string($namebits[0], $namebits[1]);
721 $child->titleidentifier
= implode(',', $namebits);
724 if (empty($child->title
)) {
725 // Use it as is, don't even clean it.
726 $child->title
= $bits[0];
727 $child->titleidentifier
= str_replace(" ", "-", $bits[0]);
731 if (!array_key_exists(1, $bits) or empty($bits[1])) {
732 // Set the url to null, and set the itemtype to invalid.
734 $child->itemtype
= "invalid";
736 // Nasty hack to replace the grades with the direct url.
737 if (strpos($bits[1], '/grade/report/mygrades.php') !== false) {
738 $bits[1] = user_mygrades_url();
741 // Make sure the url is a moodle url.
742 $bits[1] = new moodle_url(trim($bits[1]));
744 $child->url
= $bits[1];
748 if (!array_key_exists(2, $bits) or empty($bits[2])) {
750 $child->pix
= $pixpath;
752 // Check for the specified image existing.
753 $pixpath = "t/" . $bits[2];
754 if ($page->theme
->resolve_image_location($pixpath, 'moodle', true)) {
756 $child->pix
= $pixpath;
758 // Treat it like a URL.
760 $child->imgsrc
= $bits[2];
764 // Add this child to the list of children.
765 $children[] = $child;
771 * Get a list of essential user navigation items.
773 * @param stdclass $user user object.
774 * @param moodle_page $page page object.
775 * @param array $options associative array.
777 * - avatarsize=35 (size of avatar image)
778 * @return stdClass $returnobj navigation information object, where:
780 * $returnobj->navitems array array of links where each link is a
781 * stdClass with fields url, title, and
783 * $returnobj->metadata array array of useful user metadata to be
784 * used when constructing navigation;
788 * asotherrole bool whether viewing as another role
789 * rolename string name of the role
792 * These fields are for the currently-logged in user, or for
793 * the user that the real user is currently logged in as.
795 * userid int the id of the user in question
796 * userfullname string the user's full name
797 * userprofileurl moodle_url the url of the user's profile
798 * useravatar string a HTML fragment - the rendered
799 * user_picture for this user
800 * userloginfail string an error string denoting the number
801 * of login failures since last login
804 * These fields are for when asotheruser is true, and
805 * correspond to the underlying "real user".
807 * asotheruser bool whether viewing as another user
808 * realuserid int the id of the user in question
809 * realuserfullname string the user's full name
810 * realuserprofileurl moodle_url the url of the user's profile
811 * realuseravatar string a HTML fragment - the rendered
812 * user_picture for this user
814 * MNET PROVIDER FIELDS
815 * asmnetuser bool whether viewing as a user from an
817 * mnetidprovidername string name of the MNet provider
818 * mnetidproviderwwwroot string URL of the MNet provider
820 function user_get_user_navigation_info($user, $page, $options = array()) {
821 global $OUTPUT, $DB, $SESSION, $CFG;
823 $returnobject = new stdClass();
824 $returnobject->navitems
= array();
825 $returnobject->metadata
= array();
827 $course = $page->course
;
829 // Query the environment.
830 $context = context_course
::instance($course->id
);
832 // Get basic user metadata.
833 $returnobject->metadata
['userid'] = $user->id
;
834 $returnobject->metadata
['userfullname'] = fullname($user, true);
835 $returnobject->metadata
['userprofileurl'] = new moodle_url('/user/profile.php', array(
839 $avataroptions = array('link' => false, 'visibletoscreenreaders' => false);
840 if (!empty($options['avatarsize'])) {
841 $avataroptions['size'] = $options['avatarsize'];
843 $returnobject->metadata
['useravatar'] = $OUTPUT->user_picture (
844 $user, $avataroptions
846 // Build a list of items for a regular user.
848 // Query MNet status.
849 if ($returnobject->metadata
['asmnetuser'] = is_mnet_remote_user($user)) {
850 $mnetidprovider = $DB->get_record('mnet_host', array('id' => $user->mnethostid
));
851 $returnobject->metadata
['mnetidprovidername'] = $mnetidprovider->name
;
852 $returnobject->metadata
['mnetidproviderwwwroot'] = $mnetidprovider->wwwroot
;
855 // Did the user just log in?
856 if (isset($SESSION->justloggedin
)) {
857 // Don't unset this flag as login_info still needs it.
858 if (!empty($CFG->displayloginfailures
)) {
859 // Don't reset the count either, as login_info() still needs it too.
860 if ($count = user_count_login_failures($user, false)) {
862 // Get login failures string.
864 $a->attempts
= html_writer
::tag('span', $count, array('class' => 'value'));
865 $returnobject->metadata
['userloginfail'] =
866 get_string('failedloginattempts', '', $a);
873 $myhome = new stdClass();
874 $myhome->itemtype
= 'link';
875 $myhome->url
= new moodle_url('/my/');
876 $myhome->title
= get_string('mymoodle', 'admin');
877 $myhome->titleidentifier
= 'mymoodle,admin';
878 $myhome->pix
= "i/dashboard";
879 $returnobject->navitems
[] = $myhome;
881 // Links: My Profile.
882 $myprofile = new stdClass();
883 $myprofile->itemtype
= 'link';
884 $myprofile->url
= new moodle_url('/user/profile.php', array('id' => $user->id
));
885 $myprofile->title
= get_string('profile');
886 $myprofile->titleidentifier
= 'profile,moodle';
887 $myprofile->pix
= "i/user";
888 $returnobject->navitems
[] = $myprofile;
890 $returnobject->metadata
['asotherrole'] = false;
892 // Before we add the last items (usually a logout + switch role link), add any
893 // custom-defined items.
894 $customitems = user_convert_text_to_menu_items($CFG->customusermenuitems
, $page);
895 foreach ($customitems as $item) {
896 $returnobject->navitems
[] = $item;
900 if ($returnobject->metadata
['asotheruser'] = \core\session\manager
::is_loggedinas()) {
901 $realuser = \core\session\manager
::get_realuser();
903 // Save values for the real user, as $user will be full of data for the
904 // user the user is disguised as.
905 $returnobject->metadata
['realuserid'] = $realuser->id
;
906 $returnobject->metadata
['realuserfullname'] = fullname($realuser, true);
907 $returnobject->metadata
['realuserprofileurl'] = new moodle_url('/user/profile.php', array(
908 'id' => $realuser->id
910 $returnobject->metadata
['realuseravatar'] = $OUTPUT->user_picture($realuser, $avataroptions);
912 // Build a user-revert link.
913 $userrevert = new stdClass();
914 $userrevert->itemtype
= 'link';
915 $userrevert->url
= new moodle_url('/course/loginas.php', array(
917 'sesskey' => sesskey()
919 $userrevert->pix
= "a/logout";
920 $userrevert->title
= get_string('logout');
921 $userrevert->titleidentifier
= 'logout,moodle';
922 $returnobject->navitems
[] = $userrevert;
926 // Build a logout link.
927 $logout = new stdClass();
928 $logout->itemtype
= 'link';
929 $logout->url
= new moodle_url('/login/logout.php', array('sesskey' => sesskey()));
930 $logout->pix
= "a/logout";
931 $logout->title
= get_string('logout');
932 $logout->titleidentifier
= 'logout,moodle';
933 $returnobject->navitems
[] = $logout;
936 if (is_role_switched($course->id
)) {
937 if ($role = $DB->get_record('role', array('id' => $user->access
['rsw'][$context->path
]))) {
938 // Build role-return link instead of logout link.
939 $rolereturn = new stdClass();
940 $rolereturn->itemtype
= 'link';
941 $rolereturn->url
= new moodle_url('/course/switchrole.php', array(
943 'sesskey' => sesskey(),
945 'returnurl' => $page->url
->out_as_local_url(false)
947 $rolereturn->pix
= "a/logout";
948 $rolereturn->title
= get_string('switchrolereturn');
949 $rolereturn->titleidentifier
= 'switchrolereturn,moodle';
950 $returnobject->navitems
[] = $rolereturn;
952 $returnobject->metadata
['asotherrole'] = true;
953 $returnobject->metadata
['rolename'] = role_get_name($role, $context);
957 // Build switch role link.
958 $roles = get_switchable_roles($context);
959 if (is_array($roles) && (count($roles) > 0)) {
960 $switchrole = new stdClass();
961 $switchrole->itemtype
= 'link';
962 $switchrole->url
= new moodle_url('/course/switchrole.php', array(
965 'returnurl' => $page->url
->out_as_local_url(false)
967 $switchrole->pix
= "i/switchrole";
968 $switchrole->title
= get_string('switchroleto');
969 $switchrole->titleidentifier
= 'switchroleto,moodle';
970 $returnobject->navitems
[] = $switchrole;
974 return $returnobject;
978 * Add password to the list of used hashes for this user.
980 * This is supposed to be used from:
981 * 1/ change own password form
982 * 2/ password reset process
983 * 3/ user signup in auth plugins if password changing supported
985 * @param int $userid user id
986 * @param string $password plaintext password
989 function user_add_password_history($userid, $password) {
992 if (empty($CFG->passwordreuselimit
) or $CFG->passwordreuselimit
< 0) {
996 // Note: this is using separate code form normal password hashing because
997 // we need to have this under control in the future. Also the auth
998 // plugin might not store the passwords locally at all.
1000 $record = new stdClass();
1001 $record->userid
= $userid;
1002 $record->hash
= password_hash($password, PASSWORD_DEFAULT
);
1003 $record->timecreated
= time();
1004 $DB->insert_record('user_password_history', $record);
1007 $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
1008 foreach ($records as $record) {
1010 if ($i > $CFG->passwordreuselimit
) {
1011 $DB->delete_records('user_password_history', array('id' => $record->id
));
1017 * Was this password used before on change or reset password page?
1019 * The $CFG->passwordreuselimit setting determines
1020 * how many times different password needs to be used
1021 * before allowing previously used password again.
1023 * @param int $userid user id
1024 * @param string $password plaintext password
1025 * @return bool true if password reused
1027 function user_is_previously_used_password($userid, $password) {
1030 if (empty($CFG->passwordreuselimit
) or $CFG->passwordreuselimit
< 0) {
1037 $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
1038 foreach ($records as $record) {
1040 if ($i > $CFG->passwordreuselimit
) {
1041 $DB->delete_records('user_password_history', array('id' => $record->id
));
1044 // NOTE: this is slow but we cannot compare the hashes directly any more.
1045 if (password_verify($password, $record->hash
)) {
1054 * Remove a user device from the Moodle database (for PUSH notifications usually).
1056 * @param string $uuid The device UUID.
1057 * @param string $appid The app id. If empty all the devices matching the UUID for the user will be removed.
1058 * @return bool true if removed, false if the device didn't exists in the database
1061 function user_remove_user_device($uuid, $appid = "") {
1064 $conditions = array('uuid' => $uuid, 'userid' => $USER->id
);
1065 if (!empty($appid)) {
1066 $conditions['appid'] = $appid;
1069 if (!$DB->count_records('user_devices', $conditions)) {
1073 $DB->delete_records('user_devices', $conditions);
1079 * Trigger user_list_viewed event.
1081 * @param stdClass $course course object
1082 * @param stdClass $context course context object
1085 function user_list_view($course, $context) {
1087 $event = \core\event\user_list_viewed
::create(array(
1088 'objectid' => $course->id
,
1089 'courseid' => $course->id
,
1090 'context' => $context,
1092 'courseshortname' => $course->shortname
,
1093 'coursefullname' => $course->fullname
1100 * Returns the url to use for the "Grades" link in the user navigation.
1102 * @param int $userid The user's ID.
1103 * @param int $courseid The course ID if available.
1104 * @return mixed A URL to be directed to for "Grades".
1106 function user_mygrades_url($userid = null, $courseid = SITEID
) {
1109 if (isset($CFG->grade_mygrades_report
) && $CFG->grade_mygrades_report
!= 'external') {
1110 if (isset($userid) && $USER->id
!= $userid) {
1111 // Send to the gradebook report.
1112 $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report
. '/index.php',
1113 array('id' => $courseid, 'userid' => $userid));
1115 $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report
. '/index.php');
1117 } else if (isset($CFG->grade_mygrades_report
) && $CFG->grade_mygrades_report
== 'external'
1118 && !empty($CFG->gradereport_mygradeurl
)) {
1119 $url = $CFG->gradereport_mygradeurl
;
1121 $url = $CFG->wwwroot
;
1127 * Check if the current user has permission to view details of the supplied user.
1129 * This function supports two modes:
1130 * If the optional $course param is omitted, then this function finds all shared courses and checks whether the current user has
1131 * permission in any of them, returning true if so.
1132 * If the $course param is provided, then this function checks permissions in ONLY that course.
1134 * @param object $user The other user's details.
1135 * @param object $course if provided, only check permissions in this course.
1136 * @param context $usercontext The user context if available.
1137 * @return bool true for ability to view this user, else false.
1139 function user_can_view_profile($user, $course = null, $usercontext = null) {
1142 if ($user->deleted
) {
1146 // Do we need to be logged in?
1147 if (empty($CFG->forceloginforprofiles
)) {
1150 if (!isloggedin() ||
isguestuser()) {
1151 // User is not logged in and forceloginforprofile is set, we need to return now.
1156 // Current user can always view their profile.
1157 if ($USER->id
== $user->id
) {
1161 // Course contacts have visible profiles always.
1162 if (has_coursecontact_role($user->id
)) {
1166 // If we're only checking the capabilities in the single provided course.
1167 if (isset($course)) {
1168 // Confirm that $user is enrolled in the $course we're checking.
1169 if (is_enrolled(context_course
::instance($course->id
), $user)) {
1170 $userscourses = array($course);
1173 // Else we're checking whether the current user can view $user's profile anywhere, so check user context first.
1174 if (empty($usercontext)) {
1175 $usercontext = context_user
::instance($user->id
);
1177 if (has_capability('moodle/user:viewdetails', $usercontext) ||
has_capability('moodle/user:viewalldetails', $usercontext)) {
1180 // This returns context information, so we can preload below.
1181 $userscourses = enrol_get_all_users_courses($user->id
);
1184 if (empty($userscourses)) {
1188 foreach ($userscourses as $userscourse) {
1189 context_helper
::preload_from_record($userscourse);
1190 $coursecontext = context_course
::instance($userscourse->id
);
1191 if (has_capability('moodle/user:viewdetails', $coursecontext) ||
1192 has_capability('moodle/user:viewalldetails', $coursecontext)) {
1193 if (!groups_user_groups_visible($userscourse, $user->id
)) {
1194 // Not a member of the same group.
1204 * Returns users tagged with a specified tag.
1206 * @param core_tag_tag $tag
1207 * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
1208 * are displayed on the page and the per-page limit may be bigger
1209 * @param int $fromctx context id where the link was displayed, may be used by callbacks
1210 * to display items in the same context first
1211 * @param int $ctx context id where to search for records
1212 * @param bool $rec search in subcontexts as well
1213 * @param int $page 0-based number of page being displayed
1214 * @return \core_tag\output\tagindex
1216 function user_get_tagged_users($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) {
1219 if ($ctx && $ctx != context_system
::instance()->id
) {
1222 // Users can only be displayed in system context.
1223 $usercount = $tag->count_tagged_items('core', 'user',
1224 'it.deleted=:notdeleted', array('notdeleted' => 0));
1226 $perpage = $exclusivemode ?
24 : 5;
1228 $totalpages = ceil($usercount / $perpage);
1231 $userlist = $tag->get_tagged_items('core', 'user', $page * $perpage, $perpage,
1232 'it.deleted=:notdeleted', array('notdeleted' => 0));
1233 $renderer = $PAGE->get_renderer('core', 'user');
1234 $content .= $renderer->user_list($userlist, $exclusivemode);
1237 return new core_tag\output\tagindex
($tag, 'core', 'user', $content,
1238 $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages);
1242 * Returns the SQL used by the participants table.
1244 * @param int $courseid The course id
1245 * @param int $groupid The groupid, 0 means all groups
1246 * @param int $accesssince The time since last access, 0 means any time
1247 * @param int $roleid The role id, 0 means all roles
1248 * @param int $enrolid The enrolment id, 0 means all enrolment methods will be returned.
1249 * @param int $statusid The user enrolment status, -1 means all enrolments regardless of the status will be returned, if allowed.
1250 * @param string|array $search The search that was performed, empty means perform no search
1251 * @param string $additionalwhere Any additional SQL to add to where
1252 * @param array $additionalparams The additional params
1255 function user_get_participants_sql($courseid, $groupid = 0, $accesssince = 0, $roleid = 0, $enrolid = 0, $statusid = -1,
1256 $search = '', $additionalwhere = '', $additionalparams = array()) {
1260 $context = \context_course
::instance($courseid, MUST_EXIST
);
1262 $isfrontpage = ($courseid == SITEID
);
1264 // Default filter settings. We only show active by default, especially if the user has no capability to review enrolments.
1266 $onlysuspended = false;
1267 if (has_capability('moodle/course:enrolreview', $context)) {
1268 switch ($statusid) {
1269 case ENROL_USER_ACTIVE
:
1270 // Nothing to do here.
1272 case ENROL_USER_SUSPENDED
:
1273 $onlyactive = false;
1274 $onlysuspended = true;
1277 // If the user has capability to review user enrolments, but statusid is set to -1, set $onlyactive to false.
1278 $onlyactive = false;
1283 list($esql, $params) = get_enrolled_sql($context, null, $groupid, $onlyactive, $onlysuspended, $enrolid);
1285 $joins = array('FROM {user} u');
1288 $userfields = get_extra_user_fields($context);
1289 $userfieldssql = user_picture
::fields('u', $userfields);
1292 $select = "SELECT $userfieldssql, u.lastaccess";
1293 $joins[] = "JOIN ($esql) e ON e.id = u.id"; // Everybody on the frontpage usually.
1295 $wheres[] = user_get_user_lastaccess_sql($accesssince);
1298 $select = "SELECT $userfieldssql, COALESCE(ul.timeaccess, 0) AS lastaccess";
1299 $joins[] = "JOIN ($esql) e ON e.id = u.id"; // Course enrolled users only.
1300 // Not everybody has accessed the course yet.
1301 $joins[] = 'LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = :courseid)';
1302 $params['courseid'] = $courseid;
1304 $wheres[] = user_get_course_lastaccess_sql($accesssince);
1308 // Performance hacks - we preload user contexts together with accounts.
1309 $ccselect = ', ' . context_helper
::get_preload_record_columns_sql('ctx');
1310 $ccjoin = 'LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)';
1311 $params['contextlevel'] = CONTEXT_USER
;
1312 $select .= $ccselect;
1315 // Limit list to users with some role only.
1317 // We want to query both the current context and parent contexts.
1318 list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true),
1319 SQL_PARAMS_NAMED
, 'relatedctx');
1321 $wheres[] = "u.id IN (SELECT userid FROM {role_assignments} WHERE roleid = :roleid AND contextid $relatedctxsql)";
1322 $params = array_merge($params, array('roleid' => $roleid), $relatedctxparams);
1325 if (!empty($search)) {
1326 if (!is_array($search)) {
1327 $search = [$search];
1329 foreach ($search as $index => $keyword) {
1330 $searchkey1 = 'search' . $index . '1';
1331 $searchkey2 = 'search' . $index . '2';
1332 $searchkey3 = 'search' . $index . '3';
1333 $searchkey4 = 'search' . $index . '4';
1334 $searchkey5 = 'search' . $index . '5';
1335 $searchkey6 = 'search' . $index . '6';
1336 $searchkey7 = 'search' . $index . '7';
1338 $conditions = array();
1339 // Search by fullname.
1340 $fullname = $DB->sql_fullname('u.firstname', 'u.lastname');
1341 $conditions[] = $DB->sql_like($fullname, ':' . $searchkey1, false, false);
1344 $email = $DB->sql_like('email', ':' . $searchkey2, false, false);
1345 if (!in_array('email', $userfields)) {
1346 $maildisplay = 'maildisplay' . $index;
1347 $userid1 = 'userid' . $index . '1';
1348 // Prevent users who hide their email address from being found by others
1349 // who aren't allowed to see hidden email addresses.
1350 $email = "(". $email ." AND (" .
1351 "u.maildisplay <> :$maildisplay " .
1352 "OR u.id = :$userid1". // User can always find himself.
1354 $params[$maildisplay] = core_user
::MAILDISPLAY_HIDE
;
1355 $params[$userid1] = $USER->id
;
1357 $conditions[] = $email;
1359 // Search by idnumber.
1360 $idnumber = $DB->sql_like('idnumber', ':' . $searchkey3, false, false);
1361 if (!in_array('idnumber', $userfields)) {
1362 $userid2 = 'userid' . $index . '2';
1363 // Users who aren't allowed to see idnumbers should at most find themselves
1364 // when searching for an idnumber.
1365 $idnumber = "(". $idnumber . " AND u.id = :$userid2)";
1366 $params[$userid2] = $USER->id
;
1368 $conditions[] = $idnumber;
1370 // Search by middlename.
1371 $middlename = $DB->sql_like('middlename', ':' . $searchkey4, false, false);
1372 $conditions[] = $middlename;
1374 // Search by alternatename.
1375 $alternatename = $DB->sql_like('alternatename', ':' . $searchkey5, false, false);
1376 $conditions[] = $alternatename;
1378 // Search by firstnamephonetic.
1379 $firstnamephonetic = $DB->sql_like('firstnamephonetic', ':' . $searchkey6, false, false);
1380 $conditions[] = $firstnamephonetic;
1382 // Search by lastnamephonetic.
1383 $lastnamephonetic = $DB->sql_like('lastnamephonetic', ':' . $searchkey7, false, false);
1384 $conditions[] = $lastnamephonetic;
1386 $wheres[] = "(". implode(" OR ", $conditions) .") ";
1387 $params[$searchkey1] = "%$keyword%";
1388 $params[$searchkey2] = "%$keyword%";
1389 $params[$searchkey3] = "%$keyword%";
1390 $params[$searchkey4] = "%$keyword%";
1391 $params[$searchkey5] = "%$keyword%";
1392 $params[$searchkey6] = "%$keyword%";
1393 $params[$searchkey7] = "%$keyword%";
1397 if (!empty($additionalwhere)) {
1398 $wheres[] = $additionalwhere;
1399 $params = array_merge($params, $additionalparams);
1402 $from = implode("\n", $joins);
1404 $where = 'WHERE ' . implode(' AND ', $wheres);
1409 return array($select, $from, $where, $params);
1413 * Returns the total number of participants for a given course.
1415 * @param int $courseid The course id
1416 * @param int $groupid The groupid, 0 means all groups
1417 * @param int $accesssince The time since last access, 0 means any time
1418 * @param int $roleid The role id, 0 means all roles
1419 * @param int $enrolid The applied filter for the user enrolment ID.
1420 * @param int $status The applied filter for the user's enrolment status.
1421 * @param string|array $search The search that was performed, empty means perform no search
1422 * @param string $additionalwhere Any additional SQL to add to where
1423 * @param array $additionalparams The additional params
1426 function user_get_total_participants($courseid, $groupid = 0, $accesssince = 0, $roleid = 0, $enrolid = 0, $statusid = -1,
1427 $search = '', $additionalwhere = '', $additionalparams = array()) {
1430 list($select, $from, $where, $params) = user_get_participants_sql($courseid, $groupid, $accesssince, $roleid, $enrolid,
1431 $statusid, $search, $additionalwhere, $additionalparams);
1433 return $DB->count_records_sql("SELECT COUNT(u.id) $from $where", $params);
1437 * Returns the participants for a given course.
1439 * @param int $courseid The course id
1440 * @param int $groupid The group id
1441 * @param int $accesssince The time since last access
1442 * @param int $roleid The role id
1443 * @param int $enrolid The applied filter for the user enrolment ID.
1444 * @param int $status The applied filter for the user's enrolment status.
1445 * @param string $search The search that was performed
1446 * @param string $additionalwhere Any additional SQL to add to where
1447 * @param array $additionalparams The additional params
1448 * @param string $sort The SQL sort
1449 * @param int $limitfrom return a subset of records, starting at this point (optional).
1450 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1451 * @return moodle_recordset
1453 function user_get_participants($courseid, $groupid = 0, $accesssince, $roleid, $enrolid = 0, $statusid, $search,
1454 $additionalwhere = '', $additionalparams = array(), $sort = '', $limitfrom = 0, $limitnum = 0) {
1457 list($select, $from, $where, $params) = user_get_participants_sql($courseid, $groupid, $accesssince, $roleid, $enrolid,
1458 $statusid, $search, $additionalwhere, $additionalparams);
1460 return $DB->get_recordset_sql("$select $from $where $sort", $params, $limitfrom, $limitnum);
1464 * Returns SQL that can be used to limit a query to a period where the user last accessed a course.
1466 * @param int $accesssince The time since last access
1467 * @param string $tableprefix
1470 function user_get_course_lastaccess_sql($accesssince = null, $tableprefix = 'ul') {
1471 if (empty($accesssince)) {
1475 if ($accesssince == -1) { // Never.
1476 return $tableprefix . '.timeaccess = 0';
1478 return $tableprefix . '.timeaccess != 0 AND ' . $tableprefix . '.timeaccess < ' . $accesssince;
1483 * Returns SQL that can be used to limit a query to a period where the user last accessed the system.
1485 * @param int $accesssince The time since last access
1486 * @param string $tableprefix
1489 function user_get_user_lastaccess_sql($accesssince = null, $tableprefix = 'u') {
1490 if (empty($accesssince)) {
1494 if ($accesssince == -1) { // Never.
1495 return $tableprefix . '.lastaccess = 0';
1497 return $tableprefix . '.lastaccess != 0 AND ' . $tableprefix . '.lastaccess < ' . $accesssince;
1502 * Callback for inplace editable API.
1504 * @param string $itemtype - Only user_roles is supported.
1505 * @param string $itemid - Courseid and userid separated by a :
1506 * @param string $newvalue - json encoded list of roleids.
1507 * @return \core\output\inplace_editable
1509 function core_user_inplace_editable($itemtype, $itemid, $newvalue) {
1510 if ($itemtype === 'user_roles') {
1511 return \core_user\output\user_roles_editable
::update($itemid, $newvalue);