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