Merge branch 'MDL-58716-master' of git://github.com/jleyva/moodle
[moodle.git] / user / lib.php
CommitLineData
fb79269b 1<?php
fb79269b 2// This file is part of Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
17/**
18 * External user API
19 *
a2ed6e69
SH
20 * @package core_user
21 * @copyright 2009 Moodle Pty Ltd (http://moodle.com)
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
fb79269b 23 */
24
fb79269b 25/**
26 * Creates a user
adfb459c 27 *
a2ed6e69 28 * @throws moodle_exception
bb78e249
RT
29 * @param stdClass $user user to create
30 * @param bool $updatepassword if true, authentication plugin will update password.
2b55cb1b
RT
31 * @param bool $triggerevent set false if user_created event should not be triggred.
32 * This will not affect user_password_updated event triggering.
fb79269b 33 * @return int id of the newly created user
34 */
2b55cb1b 35function user_create_user($user, $updatepassword = true, $triggerevent = true) {
ac9768fc 36 global $DB;
fb79269b 37
bb78e249 38 // Set the timecreate field to the current time.
fb79269b 39 if (!is_object($user)) {
8bf0f207 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 {
7a067206 47 if ($user->username !== core_user::clean_field($user->username, 'username')) {
45b4464c
JM
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
9f7379e9 64 // Apply default values for user preferences that are stored in users table.
7a067206 65 if (!isset($user->calendartype)) {
ac9768fc 66 $user->calendartype = core_user::get_property_default('calendartype');
8bf0f207 67 }
9f7379e9 68 if (!isset($user->maildisplay)) {
ac9768fc 69 $user->maildisplay = core_user::get_property_default('maildisplay');
9f7379e9
MG
70 }
71 if (!isset($user->mailformat)) {
ac9768fc 72 $user->mailformat = core_user::get_property_default('mailformat');
9f7379e9
MG
73 }
74 if (!isset($user->maildigest)) {
ac9768fc 75 $user->maildigest = core_user::get_property_default('maildigest');
9f7379e9
MG
76 }
77 if (!isset($user->autosubscribe)) {
ac9768fc 78 $user->autosubscribe = core_user::get_property_default('autosubscribe');
9f7379e9
MG
79 }
80 if (!isset($user->trackforums)) {
ac9768fc 81 $user->trackforums = core_user::get_property_default('trackforums');
9f7379e9 82 }
7e670032 83 if (!isset($user->lang)) {
ac9768fc 84 $user->lang = core_user::get_property_default('lang');
7e670032 85 }
9f7379e9 86
fb79269b 87 $user->timecreated = time();
bd0f26bd 88 $user->timemodified = $user->timecreated;
fb79269b 89
7a067206
SL
90 // Validate user data object.
91 $uservalidation = core_user::validate($user);
92 if ($uservalidation !== true) {
93 foreach ($uservalidation as $field => $message) {
94 debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER);
95 $user->$field = core_user::clean_field($user->$field, $field);
96 }
97 }
98
bb78e249 99 // Insert the user into the database.
fb79269b 100 $newuserid = $DB->insert_record('user', $user);
101
bb78e249
RT
102 // Create USER context for this user.
103 $usercontext = context_user::instance($newuserid);
b6dcb7d9 104
bb78e249 105 // Update user password if necessary.
adfb459c 106 if (isset($userpassword)) {
bb78e249
RT
107 // Get full database user row, in case auth is default.
108 $newuser = $DB->get_record('user', array('id' => $newuserid));
adfb459c
JM
109 $authplugin = get_auth_plugin($newuser->auth);
110 $authplugin->user_update_password($newuser, $userpassword);
111 }
112
2b55cb1b
RT
113 // Trigger event If required.
114 if ($triggerevent) {
115 \core\event\user_created::create_from_userid($newuserid)->trigger();
116 }
adfb459c 117
fb79269b 118 return $newuserid;
fb79269b 119}
120
121/**
122 * Update a user with a user object (will compare against the ID)
adfb459c 123 *
a2ed6e69 124 * @throws moodle_exception
bb78e249
RT
125 * @param stdClass $user the user to update
126 * @param bool $updatepassword if true, authentication plugin will update password.
2b55cb1b
RT
127 * @param bool $triggerevent set false if user_updated event should not be triggred.
128 * This will not affect user_password_updated event triggering.
fb79269b 129 */
2b55cb1b 130function user_update_user($user, $updatepassword = true, $triggerevent = true) {
fb79269b 131 global $DB;
bd0f26bd 132
a2ed6e69 133 // Set the timecreate field to the current time.
bd0f26bd 134 if (!is_object($user)) {
8bf0f207 135 $user = (object) $user;
bd0f26bd 136 }
adfb459c 137
a2ed6e69 138 // Check username.
45b4464c 139 if (isset($user->username)) {
2f1e464a 140 if ($user->username !== core_text::strtolower($user->username)) {
45b4464c
JM
141 throw new moodle_exception('usernamelowercase');
142 } else {
7a067206 143 if ($user->username !== core_user::clean_field($user->username, 'username')) {
45b4464c
JM
144 throw new moodle_exception('invalidusername');
145 }
146 }
147 }
148
bb78e249
RT
149 // Unset password here, for updating later, if password update is required.
150 if ($updatepassword && isset($user->password)) {
adfb459c 151
a2ed6e69 152 // Check password toward the password policy.
adfb459c
JM
153 if (!check_password_policy($user->password, $errmsg)) {
154 throw new moodle_exception($errmsg);
155 }
156
9e63c0ff
FS
157 $passwd = $user->password;
158 unset($user->password);
159 }
bd0f26bd 160
8bf0f207 161 // Make sure calendartype, if set, is valid.
7a067206 162 if (empty($user->calendartype)) {
8bf0f207
MN
163 // Unset this variable, must be an empty string, which we do not want to update the calendartype to.
164 unset($user->calendartype);
165 }
166
bd0f26bd 167 $user->timemodified = time();
7a067206
SL
168
169 // Validate user data object.
170 $uservalidation = core_user::validate($user);
171 if ($uservalidation !== true) {
172 foreach ($uservalidation as $field => $message) {
173 debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER);
174 $user->$field = core_user::clean_field($user->$field, $field);
175 }
176 }
177
fb79269b 178 $DB->update_record('user', $user);
b6dcb7d9 179
bb78e249
RT
180 if ($updatepassword) {
181 // Get full user record.
182 $updateduser = $DB->get_record('user', array('id' => $user->id));
9e63c0ff 183
a2ed6e69 184 // If password was set, then update its hash.
bb78e249
RT
185 if (isset($passwd)) {
186 $authplugin = get_auth_plugin($updateduser->auth);
187 if ($authplugin->can_change_password()) {
188 $authplugin->user_update_password($updateduser, $passwd);
189 }
adfb459c
JM
190 }
191 }
2b55cb1b
RT
192 // Trigger event if required.
193 if ($triggerevent) {
194 \core\event\user_updated::create_from_userid($user->id)->trigger();
195 }
adfb459c 196}
fb79269b 197
198/**
199 * Marks user deleted in internal user database and notifies the auth plugin.
200 * Also unenrols user from all roles and does other cleanup.
201 *
202 * @todo Decide if this transaction is really needed (look for internal TODO:)
203 * @param object $user Userobject before delete (without system magic quotes)
204 * @return boolean success
205 */
206function user_delete_user($user) {
45fb2cf8 207 return delete_user($user);
fb79269b 208}
209
210/**
211 * Get users by id
fb79269b 212 *
a2ed6e69
SH
213 * @param array $userids id of users to retrieve
214 * @return array
fb79269b 215 */
216function user_get_users_by_id($userids) {
217 global $DB;
218 return $DB->get_records_list('user', 'id', $userids);
219}
b1627a92 220
61c8e0d7
FM
221/**
222 * Returns the list of default 'displayable' fields
223 *
224 * Contains database field names but also names used to generate information, such as enrolledcourses
225 *
226 * @return array of user fields
227 */
228function user_get_default_fields() {
229 return array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
230 'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
231 'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
232 'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
233 'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
511db621 234 'groups', 'roles', 'preferences', 'enrolledcourses', 'suspended'
61c8e0d7
FM
235 );
236}
01479290
DC
237
238/**
239 *
a2ed6e69 240 * Give user record from mdl_user, build an array contains all user details.
93ce0e82
JM
241 *
242 * Warning: description file urls are 'webservice/pluginfile.php' is use.
243 * it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
244 *
a2ed6e69 245 * @throws moodle_exception
01479290 246 * @param stdClass $user user record from mdl_user
01479290 247 * @param stdClass $course moodle course
ad7612f5 248 * @param array $userfields required fields
d6731600 249 * @return array|null
01479290 250 */
ad7612f5 251function user_get_user_details($user, $course = null, array $userfields = array()) {
1f7273af 252 global $USER, $DB, $CFG, $PAGE;
a2ed6e69
SH
253 require_once($CFG->dirroot . "/user/profile/lib.php"); // Custom field library.
254 require_once($CFG->dirroot . "/lib/filelib.php"); // File handling on description and friends.
01479290 255
61c8e0d7 256 $defaultfields = user_get_default_fields();
ad7612f5
DC
257
258 if (empty($userfields)) {
259 $userfields = $defaultfields;
260 }
261
262 foreach ($userfields as $thefield) {
263 if (!in_array($thefield, $defaultfields)) {
264 throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
265 }
266 }
267
a2ed6e69 268 // Make sure id and fullname are included.
ad7612f5
DC
269 if (!in_array('id', $userfields)) {
270 $userfields[] = 'id';
271 }
272
273 if (!in_array('fullname', $userfields)) {
274 $userfields[] = 'fullname';
275 }
276
01479290 277 if (!empty($course)) {
43731030
FM
278 $context = context_course::instance($course->id);
279 $usercontext = context_user::instance($user->id);
1e539f64 280 $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext));
01479290 281 } else {
43731030 282 $context = context_user::instance($user->id);
01479290 283 $usercontext = $context;
1e539f64 284 $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
01479290
DC
285 }
286
287 $currentuser = ($user->id == $USER->id);
288 $isadmin = is_siteadmin($USER);
289
48a7b182
JM
290 $showuseridentityfields = get_extra_user_fields($context);
291
01479290
DC
292 if (!empty($course)) {
293 $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
294 } else {
295 $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
296 }
86477112 297 $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
01479290
DC
298 if (!empty($course)) {
299 $canviewuseremail = has_capability('moodle/course:useremail', $context);
300 } else {
301 $canviewuseremail = false;
302 }
a2ed6e69 303 $cannotviewdescription = !empty($CFG->profilesforenrolledusersonly) && !$currentuser && !$DB->record_exists('role_assignments', array('userid' => $user->id));
01479290
DC
304 if (!empty($course)) {
305 $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
306 } else {
307 $canaccessallgroups = false;
308 }
309
310 if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
a2ed6e69 311 // Skip this user details.
01479290
DC
312 return null;
313 }
314
315 $userdetails = array();
316 $userdetails['id'] = $user->id;
317
07d37084
DM
318 if (in_array('username', $userfields)) {
319 if ($currentuser or has_capability('moodle/user:viewalldetails', $context)) {
320 $userdetails['username'] = $user->username;
321 }
01479290
DC
322 }
323 if ($isadmin or $canviewfullnames) {
ad7612f5
DC
324 if (in_array('firstname', $userfields)) {
325 $userdetails['firstname'] = $user->firstname;
326 }
327 if (in_array('lastname', $userfields)) {
328 $userdetails['lastname'] = $user->lastname;
329 }
01479290
DC
330 }
331 $userdetails['fullname'] = fullname($user);
332
ad7612f5
DC
333 if (in_array('customfields', $userfields)) {
334 $fields = $DB->get_recordset_sql("SELECT f.*
335 FROM {user_info_field} f
336 JOIN {user_info_category} c
337 ON f.categoryid=c.id
338 ORDER BY c.sortorder ASC, f.sortorder ASC");
339 $userdetails['customfields'] = array();
340 foreach ($fields as $field) {
341 require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
342 $newfield = 'profile_field_'.$field->datatype;
343 $formfield = new $newfield($field->id, $user->id);
344 if ($formfield->is_visible() and !$formfield->is_empty()) {
f804c730 345
e96e66aa
EL
346 // TODO: Part of MDL-50728, this conditional coding must be moved to
347 // proper profile fields API so they are self-contained.
f804c730
JL
348 // We only use display_data in fields that require text formatting.
349 if ($field->datatype == 'text' or $field->datatype == 'textarea') {
350 $fieldvalue = $formfield->display_data();
351 } else {
352 // Cases: datetime, checkbox and menu.
353 $fieldvalue = $formfield->data;
354 }
355
ad7612f5 356 $userdetails['customfields'][] =
f804c730 357 array('name' => $formfield->field->name, 'value' => $fieldvalue,
ad7612f5
DC
358 'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
359 }
360 }
361 $fields->close();
a2ed6e69 362 // Unset customfields if it's empty.
ad7612f5
DC
363 if (empty($userdetails['customfields'])) {
364 unset($userdetails['customfields']);
01479290 365 }
01479290
DC
366 }
367
a2ed6e69 368 // Profile image.
ad7612f5 369 if (in_array('profileimageurl', $userfields)) {
1f7273af
DP
370 $userpicture = new user_picture($user);
371 $userpicture->size = 1; // Size f1.
372 $userdetails['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
ad7612f5
DC
373 }
374 if (in_array('profileimageurlsmall', $userfields)) {
1f7273af
DP
375 if (!isset($userpicture)) {
376 $userpicture = new user_picture($user);
377 }
378 $userpicture->size = 0; // Size f2.
379 $userdetails['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
ad7612f5 380 }
01479290 381
a2ed6e69 382 // Hidden user field.
01479290
DC
383 if ($canviewhiddenuserfields) {
384 $hiddenfields = array();
a2ed6e69
SH
385 // Address, phone1 and phone2 not appears in hidden fields list but require viewhiddenfields capability
386 // according to user/profile.php.
cb805753 387 if (!empty($user->address) && in_array('address', $userfields)) {
01479290
DC
388 $userdetails['address'] = $user->address;
389 }
01479290
DC
390 } else {
391 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
392 }
393
cb805753 394 if (!empty($user->phone1) && in_array('phone1', $userfields) &&
58f739c5 395 (in_array('phone1', $showuseridentityfields) or $canviewhiddenuserfields)) {
48a7b182
JM
396 $userdetails['phone1'] = $user->phone1;
397 }
cb805753 398 if (!empty($user->phone2) && in_array('phone2', $userfields) &&
58f739c5 399 (in_array('phone2', $showuseridentityfields) or $canviewhiddenuserfields)) {
48a7b182
JM
400 $userdetails['phone2'] = $user->phone2;
401 }
402
acf64596
JM
403 if (isset($user->description) &&
404 ((!isset($hiddenfields['description']) && !$cannotviewdescription) or $isadmin)) {
405 if (in_array('description', $userfields)) {
406 // Always return the descriptionformat if description is requested.
407 list($userdetails['description'], $userdetails['descriptionformat']) =
408 external_format_text($user->description, $user->descriptionformat,
409 $usercontext->id, 'user', 'profile', null);
01479290
DC
410 }
411 }
412
ad7612f5 413 if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
01479290
DC
414 $userdetails['country'] = $user->country;
415 }
416
ad7612f5 417 if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
01479290
DC
418 $userdetails['city'] = $user->city;
419 }
420
ad7612f5 421 if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
01479290
DC
422 $url = $user->url;
423 if (strpos($user->url, '://') === false) {
424 $url = 'http://'. $url;
425 }
426 $user->url = clean_param($user->url, PARAM_URL);
427 $userdetails['url'] = $user->url;
428 }
429
ad7612f5 430 if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
01479290
DC
431 $userdetails['icq'] = $user->icq;
432 }
433
ad7612f5 434 if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
01479290
DC
435 $userdetails['skype'] = $user->skype;
436 }
ad7612f5 437 if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
01479290
DC
438 $userdetails['yahoo'] = $user->yahoo;
439 }
ad7612f5 440 if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
01479290
DC
441 $userdetails['aim'] = $user->aim;
442 }
ad7612f5 443 if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
01479290
DC
444 $userdetails['msn'] = $user->msn;
445 }
511db621 446 if (in_array('suspended', $userfields) && (!isset($hiddenfields['suspended']) or $isadmin)) {
84900149 447 $userdetails['suspended'] = (bool)$user->suspended;
511db621 448 }
01479290 449
ad7612f5 450 if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
01479290
DC
451 if ($user->firstaccess) {
452 $userdetails['firstaccess'] = $user->firstaccess;
453 } else {
454 $userdetails['firstaccess'] = 0;
455 }
456 }
ad7612f5 457 if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
01479290
DC
458 if ($user->lastaccess) {
459 $userdetails['lastaccess'] = $user->lastaccess;
460 } else {
461 $userdetails['lastaccess'] = 0;
462 }
463 }
464
a2ed6e69
SH
465 if (in_array('email', $userfields) && ($isadmin // The admin is allowed the users email.
466 or $currentuser // Of course the current user is as well.
467 or $canviewuseremail // This is a capability in course context, it will be false in usercontext.
58f739c5 468 or in_array('email', $showuseridentityfields)
01479290 469 or $user->maildisplay == 1
ca774c94 470 or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
90e30b96 471 $userdetails['email'] = $user->email;
01479290
DC
472 }
473
c4e868d5 474 if (in_array('interests', $userfields)) {
e11d7380 475 $interests = core_tag_tag::get_item_tags_array('core', 'user', $user->id, core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
c4e868d5
MG
476 if ($interests) {
477 $userdetails['interests'] = join(', ', $interests);
01479290
DC
478 }
479 }
480
a2ed6e69 481 // Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
07d37084
DM
482 if (in_array('idnumber', $userfields) && $user->idnumber) {
483 if (in_array('idnumber', $showuseridentityfields) or $currentuser or
484 has_capability('moodle/user:viewalldetails', $context)) {
3a3f3b22
CW
485 $userdetails['idnumber'] = $user->idnumber;
486 }
48a7b182 487 }
07d37084
DM
488 if (in_array('institution', $userfields) && $user->institution) {
489 if (in_array('institution', $showuseridentityfields) or $currentuser or
490 has_capability('moodle/user:viewalldetails', $context)) {
01479290
DC
491 $userdetails['institution'] = $user->institution;
492 }
48a7b182 493 }
07d37084
DM
494 // Isset because it's ok to have department 0.
495 if (in_array('department', $userfields) && isset($user->department)) {
496 if (in_array('department', $showuseridentityfields) or $currentuser or
497 has_capability('moodle/user:viewalldetails', $context)) {
01479290
DC
498 $userdetails['department'] = $user->department;
499 }
500 }
501
ad7612f5 502 if (in_array('roles', $userfields) && !empty($course)) {
a2ed6e69 503 // Not a big secret.
01479290
DC
504 $roles = get_user_roles($context, $user->id, false);
505 $userdetails['roles'] = array();
506 foreach ($roles as $role) {
507 $userdetails['roles'][] = array(
508 'roleid' => $role->roleid,
509 'name' => $role->name,
510 'shortname' => $role->shortname,
511 'sortorder' => $role->sortorder
512 );
513 }
514 }
515
a2ed6e69 516 // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group.
ad7612f5 517 if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
93ce0e82
JM
518 $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid,
519 'g.id, g.name,g.description,g.descriptionformat');
01479290
DC
520 $userdetails['groups'] = array();
521 foreach ($usergroups as $group) {
93ce0e82
JM
522 list($group->description, $group->descriptionformat) =
523 external_format_text($group->description, $group->descriptionformat,
524 $context->id, 'group', 'description', $group->id);
a2ed6e69
SH
525 $userdetails['groups'][] = array('id' => $group->id, 'name' => $group->name,
526 'description' => $group->description, 'descriptionformat' => $group->descriptionformat);
01479290
DC
527 }
528 }
a2ed6e69 529 // List of courses where the user is enrolled.
ad7612f5 530 if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
01479290
DC
531 $enrolledcourses = array();
532 if ($mycourses = enrol_get_users_courses($user->id, true)) {
533 foreach ($mycourses as $mycourse) {
534 if ($mycourse->category) {
43731030 535 $coursecontext = context_course::instance($mycourse->id);
01479290
DC
536 $enrolledcourse = array();
537 $enrolledcourse['id'] = $mycourse->id;
43ff71a0 538 $enrolledcourse['fullname'] = format_string($mycourse->fullname, true, array('context' => $coursecontext));
8ebbb06a 539 $enrolledcourse['shortname'] = format_string($mycourse->shortname, true, array('context' => $coursecontext));
01479290
DC
540 $enrolledcourses[] = $enrolledcourse;
541 }
542 }
543 $userdetails['enrolledcourses'] = $enrolledcourses;
544 }
545 }
546
a2ed6e69 547 // User preferences.
ad7612f5 548 if (in_array('preferences', $userfields) && $currentuser) {
01479290
DC
549 $preferences = array();
550 $userpreferences = get_user_preferences();
a2ed6e69 551 foreach ($userpreferences as $prefname => $prefvalue) {
01479290 552 $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
a2ed6e69
SH
553 }
554 $userdetails['preferences'] = $preferences;
01479290 555 }
ad7612f5 556
b80caca1
DTR
557 if ($currentuser or has_capability('moodle/user:viewalldetails', $context)) {
558 $extrafields = ['auth', 'confirmed', 'lang', 'theme', 'timezone', 'mailformat'];
559 foreach ($extrafields as $extrafield) {
560 if (in_array($extrafield, $userfields) && isset($user->$extrafield)) {
561 $userdetails[$extrafield] = $user->$extrafield;
562 }
563 }
564 }
565
01479290
DC
566 return $userdetails;
567}
568
86477112
FS
569/**
570 * Tries to obtain user details, either recurring directly to the user's system profile
c70b9853 571 * or through one of the user's course enrollments (course profile).
86477112 572 *
a2ed6e69 573 * @param stdClass $user The user.
c70b9853 574 * @return array if unsuccessful or the allowed user details.
86477112
FS
575 */
576function user_get_user_details_courses($user) {
577 global $USER;
578 $userdetails = null;
579
a2ed6e69 580 // Get the courses that the user is enrolled in (only active).
86477112
FS
581 $courses = enrol_get_users_courses($user->id, true);
582
c70b9853
JM
583 $systemprofile = false;
584 if (can_view_user_details_cap($user) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
585 $systemprofile = true;
586 }
86477112 587
c70b9853 588 // Try using system profile.
86477112
FS
589 if ($systemprofile) {
590 $userdetails = user_get_user_details($user, null);
591 } else {
c70b9853 592 // Try through course profile.
86477112 593 foreach ($courses as $course) {
854859e1 594 if (can_view_user_details_cap($user, $course) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
86477112
FS
595 $userdetails = user_get_user_details($user, $course);
596 }
597 }
598 }
599
600 return $userdetails;
601}
602
603/**
c70b9853
JM
604 * Check if $USER have the necessary capabilities to obtain user details.
605 *
a2ed6e69
SH
606 * @param stdClass $user
607 * @param stdClass $course if null then only consider system profile otherwise also consider the course's profile.
c70b9853 608 * @return bool true if $USER can view user details.
86477112
FS
609 */
610function can_view_user_details_cap($user, $course = null) {
c70b9853 611 // Check $USER has the capability to view the user details at user context.
bea86e84 612 $usercontext = context_user::instance($user->id);
c70b9853
JM
613 $result = has_capability('moodle/user:viewdetails', $usercontext);
614 // Otherwise can $USER see them at course context.
615 if (!$result && !empty($course)) {
bea86e84 616 $context = context_course::instance($course->id);
c70b9853 617 $result = has_capability('moodle/user:viewdetails', $context);
86477112
FS
618 }
619 return $result;
620}
621
b1627a92
DC
622/**
623 * Return a list of page types
624 * @param string $pagetype current page type
625 * @param stdClass $parentcontext Block's parent context
626 * @param stdClass $currentcontext Current context of block
a2ed6e69 627 * @return array
b1627a92 628 */
b38e2e28 629function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
a2ed6e69 630 return array('user-profile' => get_string('page-user-profile', 'pagetype'));
d6731600 631}
52dc1de7
AA
632
633/**
634 * Count the number of failed login attempts for the given user, since last successful login.
635 *
636 * @param int|stdclass $user user id or object.
637 * @param bool $reset Resets failed login count, if set to true.
638 *
639 * @return int number of failed login attempts since the last successful login.
640 */
641function user_count_login_failures($user, $reset = true) {
642 global $DB;
643
644 if (!is_object($user)) {
645 $user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST);
646 }
647 if ($user->deleted) {
648 // Deleted user, nothing to do.
649 return 0;
650 }
651 $count = get_user_preferences('login_failed_count_since_success', 0, $user);
652 if ($reset) {
653 set_user_preference('login_failed_count_since_success', 0, $user);
654 }
655 return $count;
656}
657
b865a3e2 658/**
22893d6f
JC
659 * Converts a string into a flat array of menu items, where each menu items is a
660 * stdClass with fields type, url, title, pix, and imgsrc.
b865a3e2
JC
661 *
662 * @param string $text the menu items definition
663 * @param moodle_page $page the current page
664 * @return array
665 */
666function user_convert_text_to_menu_items($text, $page) {
667 global $OUTPUT, $CFG;
668
669 $lines = explode("\n", $text);
670 $items = array();
671 $lastchild = null;
672 $lastdepth = null;
673 $lastsort = 0;
088fcf90 674 $children = array();
b865a3e2
JC
675 foreach ($lines as $line) {
676 $line = trim($line);
677 $bits = explode('|', $line, 3);
22893d6f
JC
678 $itemtype = 'link';
679 if (preg_match("/^#+$/", $line)) {
680 $itemtype = 'divider';
681 } else if (!array_key_exists(0, $bits) or empty($bits[0])) {
b865a3e2
JC
682 // Every item must have a name to be valid.
683 continue;
684 } else {
685 $bits[0] = ltrim($bits[0], '-');
686 }
687
688 // Create the child.
689 $child = new stdClass();
22893d6f
JC
690 $child->itemtype = $itemtype;
691 if ($itemtype === 'divider') {
692 // Add the divider to the list of children and skip link
693 // processing.
694 $children[] = $child;
695 continue;
696 }
b865a3e2
JC
697
698 // Name processing.
699 $namebits = explode(',', $bits[0], 2);
700 if (count($namebits) == 2) {
06c27531
AN
701 // Check the validity of the identifier part of the string.
702 if (clean_param($namebits[0], PARAM_STRINGID) !== '') {
703 // Treat this as a language string.
704 $child->title = get_string($namebits[0], $namebits[1]);
d7dbef73 705 $child->titleidentifier = implode(',', $namebits);
06c27531
AN
706 }
707 }
708 if (empty($child->title)) {
b865a3e2
JC
709 // Use it as is, don't even clean it.
710 $child->title = $bits[0];
d7dbef73 711 $child->titleidentifier = str_replace(" ", "-", $bits[0]);
b865a3e2
JC
712 }
713
714 // URL processing.
715 if (!array_key_exists(1, $bits) or empty($bits[1])) {
22893d6f 716 // Set the url to null, and set the itemtype to invalid.
b865a3e2 717 $bits[1] = null;
22893d6f 718 $child->itemtype = "invalid";
b865a3e2 719 } else {
4887d152 720 // Nasty hack to replace the grades with the direct url.
c9451960
AG
721 if (strpos($bits[1], '/grade/report/mygrades.php') !== false) {
722 $bits[1] = user_mygrades_url();
723 }
724
b865a3e2
JC
725 // Make sure the url is a moodle url.
726 $bits[1] = new moodle_url(trim($bits[1]));
727 }
728 $child->url = $bits[1];
729
730 // PIX processing.
731 $pixpath = "t/edit";
732 if (!array_key_exists(2, $bits) or empty($bits[2])) {
733 // Use the default.
734 $child->pix = $pixpath;
735 } else {
736 // Check for the specified image existing.
737 $pixpath = "t/" . $bits[2];
738 if ($page->theme->resolve_image_location($pixpath, 'moodle', true)) {
739 // Use the image.
740 $child->pix = $pixpath;
741 } else {
742 // Treat it like a URL.
743 $child->pix = null;
744 $child->imgsrc = $bits[2];
745 }
746 }
747
748 // Add this child to the list of children.
749 $children[] = $child;
750 }
751 return $children;
752}
753
6da0e4cf
JC
754/**
755 * Get a list of essential user navigation items.
756 *
757 * @param stdclass $user user object.
758 * @param moodle_page $page page object.
9dcd5035
DB
759 * @param array $options associative array.
760 * options are:
761 * - avatarsize=35 (size of avatar image)
6da0e4cf
JC
762 * @return stdClass $returnobj navigation information object, where:
763 *
764 * $returnobj->navitems array array of links where each link is a
765 * stdClass with fields url, title, and
766 * pix
767 * $returnobj->metadata array array of useful user metadata to be
768 * used when constructing navigation;
769 * fields include:
770 *
771 * ROLE FIELDS
772 * asotherrole bool whether viewing as another role
773 * rolename string name of the role
774 *
775 * USER FIELDS
776 * These fields are for the currently-logged in user, or for
777 * the user that the real user is currently logged in as.
778 *
779 * userid int the id of the user in question
780 * userfullname string the user's full name
781 * userprofileurl moodle_url the url of the user's profile
782 * useravatar string a HTML fragment - the rendered
783 * user_picture for this user
784 * userloginfail string an error string denoting the number
785 * of login failures since last login
786 *
787 * "REAL USER" FIELDS
788 * These fields are for when asotheruser is true, and
789 * correspond to the underlying "real user".
790 *
791 * asotheruser bool whether viewing as another user
792 * realuserid int the id of the user in question
793 * realuserfullname string the user's full name
794 * realuserprofileurl moodle_url the url of the user's profile
795 * realuseravatar string a HTML fragment - the rendered
796 * user_picture for this user
797 *
798 * MNET PROVIDER FIELDS
799 * asmnetuser bool whether viewing as a user from an
800 * MNet provider
801 * mnetidprovidername string name of the MNet provider
802 * mnetidproviderwwwroot string URL of the MNet provider
803 */
c68b3206 804function user_get_user_navigation_info($user, $page, $options = array()) {
6da0e4cf
JC
805 global $OUTPUT, $DB, $SESSION, $CFG;
806
807 $returnobject = new stdClass();
808 $returnobject->navitems = array();
809 $returnobject->metadata = array();
810
811 $course = $page->course;
812
813 // Query the environment.
814 $context = context_course::instance($course->id);
815
816 // Get basic user metadata.
817 $returnobject->metadata['userid'] = $user->id;
818 $returnobject->metadata['userfullname'] = fullname($user, true);
819 $returnobject->metadata['userprofileurl'] = new moodle_url('/user/profile.php', array(
820 'id' => $user->id
821 ));
9dcd5035
DB
822
823 $avataroptions = array('link' => false, 'visibletoscreenreaders' => false);
824 if (!empty($options['avatarsize'])) {
825 $avataroptions['size'] = $options['avatarsize'];
826 }
6da0e4cf 827 $returnobject->metadata['useravatar'] = $OUTPUT->user_picture (
9dcd5035 828 $user, $avataroptions
6da0e4cf
JC
829 );
830 // Build a list of items for a regular user.
831
832 // Query MNet status.
833 if ($returnobject->metadata['asmnetuser'] = is_mnet_remote_user($user)) {
834 $mnetidprovider = $DB->get_record('mnet_host', array('id' => $user->mnethostid));
835 $returnobject->metadata['mnetidprovidername'] = $mnetidprovider->name;
836 $returnobject->metadata['mnetidproviderwwwroot'] = $mnetidprovider->wwwroot;
837 }
838
839 // Did the user just log in?
840 if (isset($SESSION->justloggedin)) {
841 // Don't unset this flag as login_info still needs it.
842 if (!empty($CFG->displayloginfailures)) {
fa30dd8c
JD
843 // Don't reset the count either, as login_info() still needs it too.
844 if ($count = user_count_login_failures($user, false)) {
6da0e4cf
JC
845
846 // Get login failures string.
847 $a = new stdClass();
848 $a->attempts = html_writer::tag('span', $count, array('class' => 'value'));
849 $returnobject->metadata['userloginfail'] =
850 get_string('failedloginattempts', '', $a);
851
852 }
853 }
854 }
855
81d7de1a 856 // Links: Dashboard.
6da0e4cf 857 $myhome = new stdClass();
22893d6f 858 $myhome->itemtype = 'link';
6da0e4cf
JC
859 $myhome->url = new moodle_url('/my/');
860 $myhome->title = get_string('mymoodle', 'admin');
d7dbef73 861 $myhome->titleidentifier = 'mymoodle,admin';
f3dacc38 862 $myhome->pix = "i/dashboard";
6da0e4cf
JC
863 $returnobject->navitems[] = $myhome;
864
865 // Links: My Profile.
866 $myprofile = new stdClass();
22893d6f 867 $myprofile->itemtype = 'link';
6da0e4cf 868 $myprofile->url = new moodle_url('/user/profile.php', array('id' => $user->id));
4887d152 869 $myprofile->title = get_string('profile');
d7dbef73 870 $myprofile->titleidentifier = 'profile,moodle';
6da0e4cf
JC
871 $myprofile->pix = "i/user";
872 $returnobject->navitems[] = $myprofile;
873
6da0e4cf 874 $returnobject->metadata['asotherrole'] = false;
6da0e4cf 875
70b03eff 876 // Before we add the last items (usually a logout + switch role link), add any
7095f5be
DW
877 // custom-defined items.
878 $customitems = user_convert_text_to_menu_items($CFG->customusermenuitems, $page);
879 foreach ($customitems as $item) {
880 $returnobject->navitems[] = $item;
6da0e4cf
JC
881 }
882
70b03eff 883
6da0e4cf
JC
884 if ($returnobject->metadata['asotheruser'] = \core\session\manager::is_loggedinas()) {
885 $realuser = \core\session\manager::get_realuser();
886
887 // Save values for the real user, as $user will be full of data for the
888 // user the user is disguised as.
889 $returnobject->metadata['realuserid'] = $realuser->id;
890 $returnobject->metadata['realuserfullname'] = fullname($realuser, true);
891 $returnobject->metadata['realuserprofileurl'] = new moodle_url('/user/profile.php', array(
892 'id' => $realuser->id
893 ));
c68b3206 894 $returnobject->metadata['realuseravatar'] = $OUTPUT->user_picture($realuser, $avataroptions);
6da0e4cf
JC
895
896 // Build a user-revert link.
897 $userrevert = new stdClass();
22893d6f 898 $userrevert->itemtype = 'link';
6da0e4cf
JC
899 $userrevert->url = new moodle_url('/course/loginas.php', array(
900 'id' => $course->id,
901 'sesskey' => sesskey()
902 ));
903 $userrevert->pix = "a/logout";
904 $userrevert->title = get_string('logout');
d7dbef73 905 $userrevert->titleidentifier = 'logout,moodle';
7095f5be 906 $returnobject->navitems[] = $userrevert;
6da0e4cf 907
7095f5be 908 } else {
6da0e4cf 909
6da0e4cf
JC
910 // Build a logout link.
911 $logout = new stdClass();
22893d6f 912 $logout->itemtype = 'link';
6da0e4cf
JC
913 $logout->url = new moodle_url('/login/logout.php', array('sesskey' => sesskey()));
914 $logout->pix = "a/logout";
915 $logout->title = get_string('logout');
d7dbef73 916 $logout->titleidentifier = 'logout,moodle';
7095f5be 917 $returnobject->navitems[] = $logout;
6da0e4cf
JC
918 }
919
70b03eff
DW
920 if (is_role_switched($course->id)) {
921 if ($role = $DB->get_record('role', array('id' => $user->access['rsw'][$context->path]))) {
922 // Build role-return link instead of logout link.
923 $rolereturn = new stdClass();
924 $rolereturn->itemtype = 'link';
925 $rolereturn->url = new moodle_url('/course/switchrole.php', array(
926 'id' => $course->id,
927 'sesskey' => sesskey(),
928 'switchrole' => 0,
929 'returnurl' => $page->url->out_as_local_url(false)
930 ));
931 $rolereturn->pix = "a/logout";
932 $rolereturn->title = get_string('switchrolereturn');
933 $rolereturn->titleidentifier = 'switchrolereturn,moodle';
934 $returnobject->navitems[] = $rolereturn;
935
936 $returnobject->metadata['asotherrole'] = true;
937 $returnobject->metadata['rolename'] = role_get_name($role, $context);
b865a3e2 938
70b03eff
DW
939 }
940 } else {
7dba450f
DW
941 // Build switch role link.
942 $roles = get_switchable_roles($context);
943 if (is_array($roles) && (count($roles) > 0)) {
944 $switchrole = new stdClass();
945 $switchrole->itemtype = 'link';
946 $switchrole->url = new moodle_url('/course/switchrole.php', array(
947 'id' => $course->id,
948 'switchrole' => -1,
949 'returnurl' => $page->url->out_as_local_url(false)
950 ));
951 $switchrole->pix = "i/switchrole";
952 $switchrole->title = get_string('switchroleto');
953 $switchrole->titleidentifier = 'switchroleto,moodle';
954 $returnobject->navitems[] = $switchrole;
955 }
6da0e4cf
JC
956 }
957
958 return $returnobject;
1d658535
PS
959}
960
961/**
962 * Add password to the list of used hashes for this user.
963 *
964 * This is supposed to be used from:
965 * 1/ change own password form
966 * 2/ password reset process
967 * 3/ user signup in auth plugins if password changing supported
968 *
969 * @param int $userid user id
970 * @param string $password plaintext password
971 * @return void
972 */
973function user_add_password_history($userid, $password) {
974 global $CFG, $DB;
1d658535
PS
975
976 if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
977 return;
978 }
979
980 // Note: this is using separate code form normal password hashing because
981 // we need to have this under control in the future. Also the auth
982 // plugin might not store the passwords locally at all.
983
984 $record = new stdClass();
985 $record->userid = $userid;
986 $record->hash = password_hash($password, PASSWORD_DEFAULT);
987 $record->timecreated = time();
988 $DB->insert_record('user_password_history', $record);
989
990 $i = 0;
991 $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
992 foreach ($records as $record) {
993 $i++;
994 if ($i > $CFG->passwordreuselimit) {
995 $DB->delete_records('user_password_history', array('id' => $record->id));
996 }
997 }
998}
999
1000/**
1001 * Was this password used before on change or reset password page?
1002 *
1003 * The $CFG->passwordreuselimit setting determines
1004 * how many times different password needs to be used
1005 * before allowing previously used password again.
1006 *
1007 * @param int $userid user id
1008 * @param string $password plaintext password
1009 * @return bool true if password reused
1010 */
1011function user_is_previously_used_password($userid, $password) {
1012 global $CFG, $DB;
1d658535
PS
1013
1014 if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
1015 return false;
1016 }
1017
1018 $reused = false;
1019
1020 $i = 0;
1021 $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
1022 foreach ($records as $record) {
1023 $i++;
1024 if ($i > $CFG->passwordreuselimit) {
1025 $DB->delete_records('user_password_history', array('id' => $record->id));
1026 continue;
1027 }
1028 // NOTE: this is slow but we cannot compare the hashes directly any more.
1029 if (password_verify($password, $record->hash)) {
1030 $reused = true;
1031 }
1032 }
1033
1034 return $reused;
1035}
3221718e
JL
1036
1037/**
1038 * Remove a user device from the Moodle database (for PUSH notifications usually).
1039 *
1040 * @param string $uuid The device UUID.
1041 * @param string $appid The app id. If empty all the devices matching the UUID for the user will be removed.
1042 * @return bool true if removed, false if the device didn't exists in the database
1043 * @since Moodle 2.9
1044 */
1045function user_remove_user_device($uuid, $appid = "") {
1046 global $DB, $USER;
1047
1048 $conditions = array('uuid' => $uuid, 'userid' => $USER->id);
1049 if (!empty($appid)) {
1050 $conditions['appid'] = $appid;
1051 }
1052
1053 if (!$DB->count_records('user_devices', $conditions)) {
1054 return false;
1055 }
1056
1057 $DB->delete_records('user_devices', $conditions);
1058
1059 return true;
1060}
0ff203b6
JL
1061
1062/**
1063 * Trigger user_list_viewed event.
1064 *
1065 * @param stdClass $course course object
1066 * @param stdClass $context course context object
1067 * @since Moodle 2.9
1068 */
1069function user_list_view($course, $context) {
1070
1071 $event = \core\event\user_list_viewed::create(array(
1072 'objectid' => $course->id,
1073 'courseid' => $course->id,
1074 'context' => $context,
1075 'other' => array(
1076 'courseshortname' => $course->shortname,
1077 'coursefullname' => $course->fullname
1078 )
1079 ));
1080 $event->trigger();
1081}
c9451960
AG
1082
1083/**
4887d152 1084 * Returns the url to use for the "Grades" link in the user navigation.
c9451960
AG
1085 *
1086 * @param int $userid The user's ID.
1087 * @param int $courseid The course ID if available.
4887d152 1088 * @return mixed A URL to be directed to for "Grades".
c9451960
AG
1089 */
1090function user_mygrades_url($userid = null, $courseid = SITEID) {
1091 global $CFG, $USER;
1092 $url = null;
1093 if (isset($CFG->grade_mygrades_report) && $CFG->grade_mygrades_report != 'external') {
1094 if (isset($userid) && $USER->id != $userid) {
1095 // Send to the gradebook report.
1096 $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report . '/index.php',
1097 array('id' => $courseid, 'userid' => $userid));
1098 } else {
1099 $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report . '/index.php');
1100 }
1101 } else if (isset($CFG->grade_mygrades_report) && $CFG->grade_mygrades_report == 'external'
1102 && !empty($CFG->gradereport_mygradeurl)) {
1103 $url = $CFG->gradereport_mygradeurl;
1104 } else {
1105 $url = $CFG->wwwroot;
1106 }
1107 return $url;
1108}
66a43cd5
AG
1109
1110/**
1111 * Check if a user has the permission to viewdetails in a shared course's context.
1112 *
1113 * @param object $user The other user's details.
1114 * @param object $course Use this course to see if we have permission to see this user's profile.
1115 * @param context $usercontext The user context if available.
1116 * @return bool true for ability to view this user, else false.
1117 */
1118function user_can_view_profile($user, $course = null, $usercontext = null) {
1119 global $USER, $CFG;
1120
1121 if ($user->deleted) {
1122 return false;
1123 }
1124
58d85af2
AA
1125 // Perform some quick checks and eventually return early.
1126
66a43cd5 1127 // Number 1.
58d85af2 1128 if (empty($CFG->forceloginforprofiles)) {
66a43cd5 1129 return true;
58d85af2
AA
1130 } else {
1131 if (!isloggedin() || isguestuser()) {
1132 // User is not logged in and forceloginforprofile is set, we need to return now.
1133 return false;
1134 }
66a43cd5
AG
1135 }
1136
1137 // Number 2.
58d85af2 1138 if ($USER->id == $user->id) {
66a43cd5
AG
1139 return true;
1140 }
1141
1142 if (empty($usercontext)) {
1143 $usercontext = context_user::instance($user->id);
1144 }
1145 // Number 3.
1146 if (has_capability('moodle/user:viewdetails', $usercontext)) {
1147 return true;
1148 }
1149
1150 // Number 4.
1151 if (has_coursecontact_role($user->id)) {
1152 return true;
1153 }
1154
1155 if (isset($course)) {
1156 $sharedcourses = array($course);
1157 } else {
1158 $sharedcourses = enrol_get_shared_courses($USER->id, $user->id, true);
1159 }
3e40dc8c
AA
1160
1161 if (empty($sharedcourses)) {
1162 return false;
1163 }
1164
66a43cd5
AG
1165 foreach ($sharedcourses as $sharedcourse) {
1166 $coursecontext = context_course::instance($sharedcourse->id);
1167 if (has_capability('moodle/user:viewdetails', $coursecontext)) {
1168 if (!groups_user_groups_visible($sharedcourse, $user->id)) {
1169 // Not a member of the same group.
1170 continue;
1171 }
1172 return true;
1173 }
1174 }
1175 return false;
1176}
c4e868d5
MG
1177
1178/**
1179 * Returns users tagged with a specified tag.
1180 *
1181 * @param core_tag_tag $tag
1182 * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
1183 * are displayed on the page and the per-page limit may be bigger
1184 * @param int $fromctx context id where the link was displayed, may be used by callbacks
1185 * to display items in the same context first
1186 * @param int $ctx context id where to search for records
1187 * @param bool $rec search in subcontexts as well
1188 * @param int $page 0-based number of page being displayed
1189 * @return \core_tag\output\tagindex
1190 */
1191function user_get_tagged_users($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) {
1192 global $PAGE;
1193
1194 if ($ctx && $ctx != context_system::instance()->id) {
1195 $usercount = 0;
1196 } else {
1197 // Users can only be displayed in system context.
1198 $usercount = $tag->count_tagged_items('core', 'user',
1199 'it.deleted=:notdeleted', array('notdeleted' => 0));
1200 }
1201 $perpage = $exclusivemode ? 24 : 5;
1202 $content = '';
1203 $totalpages = ceil($usercount / $perpage);
1204
1205 if ($usercount) {
1206 $userlist = $tag->get_tagged_items('core', 'user', $page * $perpage, $perpage,
1207 'it.deleted=:notdeleted', array('notdeleted' => 0));
1208 $renderer = $PAGE->get_renderer('core', 'user');
1209 $content .= $renderer->user_list($userlist, $exclusivemode);
1210 }
1211
1212 return new core_tag\output\tagindex($tag, 'core', 'user', $content,
1213 $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages);
1214}