MDL-65308 core: change a couple of unsafe preg_quote() cases
[moodle.git] / enrol / externallib.php
blob71608dfdb36825e5389b1e80f9fad754b853dc67
1 <?php
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.
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/>.
18 /**
19 * External course participation api.
21 * This api is mostly read only, the actual enrol and unenrol
22 * support is in each enrol plugin.
24 * @package core_enrol
25 * @category external
26 * @copyright 2010 Jerome Mouneyrac
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 defined('MOODLE_INTERNAL') || die();
32 require_once("$CFG->libdir/externallib.php");
34 /**
35 * Enrol external functions
37 * @package core_enrol
38 * @category external
39 * @copyright 2011 Jerome Mouneyrac
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 * @since Moodle 2.2
43 class core_enrol_external extends external_api {
45 /**
46 * Returns description of method parameters
48 * @return external_function_parameters
49 * @since Moodle 2.4
51 public static function get_enrolled_users_with_capability_parameters() {
52 return new external_function_parameters(
53 array (
54 'coursecapabilities' => new external_multiple_structure(
55 new external_single_structure(
56 array (
57 'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'),
58 'capabilities' => new external_multiple_structure(
59 new external_value(PARAM_CAPABILITY, 'Capability name, such as mod/forum:viewdiscussion')),
62 , 'course id and associated capability name'),
63 'options' => new external_multiple_structure(
64 new external_single_structure(
65 array(
66 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
67 'value' => new external_value(PARAM_RAW, 'option value')
69 ), 'Option names:
70 * groupid (integer) return only users in this group id. Requires \'moodle/site:accessallgroups\' .
71 * onlyactive (integer) only users with active enrolments. Requires \'moodle/course:enrolreview\' .
72 * userfields (\'string, string, ...\') return only the values of these user fields.
73 * limitfrom (integer) sql limit from.
74 * limitnumber (integer) max number of users per course and capability.', VALUE_DEFAULT, array())
79 /**
80 * Return users that have the capabilities for each course specified. For each course and capability specified,
81 * a list of the users that are enrolled in the course and have that capability are returned.
83 * @param array $coursecapabilities array of course ids and associated capability names {courseid, {capabilities}}
84 * @return array An array of arrays describing users for each associated courseid and capability
85 * @since Moodle 2.4
87 public static function get_enrolled_users_with_capability($coursecapabilities, $options) {
88 global $CFG, $DB;
90 require_once($CFG->dirroot . '/course/lib.php');
91 require_once($CFG->dirroot . "/user/lib.php");
93 if (empty($coursecapabilities)) {
94 throw new invalid_parameter_exception('Parameter can not be empty');
96 $params = self::validate_parameters(self::get_enrolled_users_with_capability_parameters(),
97 array ('coursecapabilities' => $coursecapabilities, 'options'=>$options));
98 $result = array();
99 $userlist = array();
100 $groupid = 0;
101 $onlyactive = false;
102 $userfields = array();
103 $limitfrom = 0;
104 $limitnumber = 0;
105 foreach ($params['options'] as $option) {
106 switch ($option['name']) {
107 case 'groupid':
108 $groupid = (int)$option['value'];
109 break;
110 case 'onlyactive':
111 $onlyactive = !empty($option['value']);
112 break;
113 case 'userfields':
114 $thefields = explode(',', $option['value']);
115 foreach ($thefields as $f) {
116 $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
118 break;
119 case 'limitfrom' :
120 $limitfrom = clean_param($option['value'], PARAM_INT);
121 break;
122 case 'limitnumber' :
123 $limitnumber = clean_param($option['value'], PARAM_INT);
124 break;
128 foreach ($params['coursecapabilities'] as $coursecapability) {
129 $courseid = $coursecapability['courseid'];
130 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
131 $coursecontext = context_course::instance($courseid);
132 if (!$coursecontext) {
133 throw new moodle_exception('cannotfindcourse', 'error', '', null,
134 'The course id ' . $courseid . ' doesn\'t exist.');
136 if ($courseid == SITEID) {
137 $context = context_system::instance();
138 } else {
139 $context = $coursecontext;
141 try {
142 self::validate_context($context);
143 } catch (Exception $e) {
144 $exceptionparam = new stdClass();
145 $exceptionparam->message = $e->getMessage();
146 $exceptionparam->courseid = $params['courseid'];
147 throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
150 course_require_view_participants($context);
152 // The accessallgroups capability is needed to use this option.
153 if (!empty($groupid) && groups_is_member($groupid)) {
154 require_capability('moodle/site:accessallgroups', $coursecontext);
156 // The course:enrolereview capability is needed to use this option.
157 if ($onlyactive) {
158 require_capability('moodle/course:enrolreview', $coursecontext);
161 // To see the permissions of others role:review capability is required.
162 require_capability('moodle/role:review', $coursecontext);
163 foreach ($coursecapability['capabilities'] as $capability) {
164 $courseusers['courseid'] = $courseid;
165 $courseusers['capability'] = $capability;
167 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $capability, $groupid, $onlyactive);
168 $enrolledparams['courseid'] = $courseid;
170 $sql = "SELECT u.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
171 FROM {user} u
172 LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = :courseid)
173 WHERE u.id IN ($enrolledsql)
174 ORDER BY u.id ASC";
176 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
177 $users = array();
178 foreach ($enrolledusers as $courseuser) {
179 if ($userdetails = user_get_user_details($courseuser, $course, $userfields)) {
180 $users[] = $userdetails;
183 $enrolledusers->close();
184 $courseusers['users'] = $users;
185 $result[] = $courseusers;
188 return $result;
192 * Returns description of method result value
194 * @return external_multiple_structure
195 * @since Moodle 2.4
197 public static function get_enrolled_users_with_capability_returns() {
198 return new external_multiple_structure( new external_single_structure (
199 array (
200 'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'),
201 'capability' => new external_value(PARAM_CAPABILITY, 'Capability name'),
202 'users' => new external_multiple_structure(
203 new external_single_structure(
204 array(
205 'id' => new external_value(PARAM_INT, 'ID of the user'),
206 'username' => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL),
207 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
208 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
209 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
210 'email' => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL),
211 'address' => new external_value(PARAM_MULTILANG, 'Postal address', VALUE_OPTIONAL),
212 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
213 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
214 'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
215 'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
216 'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
217 'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
218 'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
219 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
220 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
221 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
222 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
223 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
224 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
225 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
226 'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
227 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
228 'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
229 'country' => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
230 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
231 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
232 'customfields' => new external_multiple_structure(
233 new external_single_structure(
234 array(
235 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
236 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
237 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
238 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
240 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
241 'groups' => new external_multiple_structure(
242 new external_single_structure(
243 array(
244 'id' => new external_value(PARAM_INT, 'group id'),
245 'name' => new external_value(PARAM_RAW, 'group name'),
246 'description' => new external_value(PARAM_RAW, 'group description'),
248 ), 'user groups', VALUE_OPTIONAL),
249 'roles' => new external_multiple_structure(
250 new external_single_structure(
251 array(
252 'roleid' => new external_value(PARAM_INT, 'role id'),
253 'name' => new external_value(PARAM_RAW, 'role name'),
254 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
255 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
257 ), 'user roles', VALUE_OPTIONAL),
258 'preferences' => new external_multiple_structure(
259 new external_single_structure(
260 array(
261 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
262 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
264 ), 'User preferences', VALUE_OPTIONAL),
265 'enrolledcourses' => new external_multiple_structure(
266 new external_single_structure(
267 array(
268 'id' => new external_value(PARAM_INT, 'Id of the course'),
269 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
270 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
272 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
274 ), 'List of users that are enrolled in the course and have the specified capability'),
281 * Returns description of method parameters
283 * @return external_function_parameters
285 public static function get_users_courses_parameters() {
286 return new external_function_parameters(
287 array(
288 'userid' => new external_value(PARAM_INT, 'user id'),
289 'returnusercount' => new external_value(PARAM_BOOL,
290 'Include count of enrolled users for each course? This can add several seconds to the response time'
291 . ' if a user is on several large courses, so set this to false if the value will not be used to'
292 . ' improve performance.',
293 VALUE_DEFAULT, true),
299 * Get list of courses user is enrolled in (only active enrolments are returned).
300 * Please note the current user must be able to access the course, otherwise the course is not included.
302 * @param int $userid
303 * @param bool $returnusercount
304 * @return array of courses
306 public static function get_users_courses($userid, $returnusercount = true) {
307 global $CFG, $USER, $DB;
309 require_once($CFG->dirroot . '/course/lib.php');
310 require_once($CFG->libdir . '/completionlib.php');
312 // Do basic automatic PARAM checks on incoming data, using params description
313 // If any problems are found then exceptions are thrown with helpful error messages
314 $params = self::validate_parameters(self::get_users_courses_parameters(),
315 ['userid' => $userid, 'returnusercount' => $returnusercount]);
316 $userid = $params['userid'];
317 $returnusercount = $params['returnusercount'];
319 $courses = enrol_get_users_courses($userid, true, '*');
320 $result = array();
322 // Get user data including last access to courses.
323 $user = get_complete_user_data('id', $userid);
324 $sameuser = $USER->id == $userid;
326 // Retrieve favourited courses (starred).
327 $favouritecourseids = array();
328 if ($sameuser) {
329 $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
330 $favourites = $ufservice->find_favourites_by_type('core_course', 'courses');
332 if ($favourites) {
333 $favouritecourseids = array_flip(array_map(
334 function($favourite) {
335 return $favourite->itemid;
336 }, $favourites));
340 foreach ($courses as $course) {
341 $context = context_course::instance($course->id, IGNORE_MISSING);
342 try {
343 self::validate_context($context);
344 } catch (Exception $e) {
345 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
346 continue;
349 if (!$sameuser and !course_can_view_participants($context)) {
350 // we need capability to view participants
351 continue;
354 if ($returnusercount) {
355 list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
356 $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids";
357 $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
360 $displayname = external_format_string(get_course_display_name_for_list($course), $context->id);
361 list($course->summary, $course->summaryformat) =
362 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
363 $course->fullname = external_format_string($course->fullname, $context->id);
364 $course->shortname = external_format_string($course->shortname, $context->id);
366 $progress = null;
367 $completed = null;
368 $completionhascriteria = false;
370 // Return only private information if the user should be able to see it.
371 if ($sameuser || completion_can_view_data($userid, $course)) {
372 if ($course->enablecompletion) {
373 $completion = new completion_info($course);
374 $completed = $completion->is_course_complete($userid);
375 $completionhascriteria = $completion->has_criteria();
376 $progress = \core_completion\progress::get_course_progress_percentage($course, $userid);
380 $lastaccess = null;
381 // Check if last access is a hidden field.
382 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
383 $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']);
384 if (!$canviewlastaccess) {
385 $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context);
388 if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) {
389 $lastaccess = $user->lastcourseaccess[$course->id];
392 $hidden = false;
393 if ($sameuser) {
394 $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0));
397 // Retrieve course overview used files.
398 $courselist = new core_course_list_element($course);
399 $overviewfiles = array();
400 foreach ($courselist->get_course_overviewfiles() as $file) {
401 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
402 $file->get_filearea(), null, $file->get_filepath(),
403 $file->get_filename())->out(false);
404 $overviewfiles[] = array(
405 'filename' => $file->get_filename(),
406 'fileurl' => $fileurl,
407 'filesize' => $file->get_filesize(),
408 'filepath' => $file->get_filepath(),
409 'mimetype' => $file->get_mimetype(),
410 'timemodified' => $file->get_timemodified(),
414 $courseresult = [
415 'id' => $course->id,
416 'shortname' => $course->shortname,
417 'fullname' => $course->fullname,
418 'displayname' => $displayname,
419 'idnumber' => $course->idnumber,
420 'visible' => $course->visible,
421 'summary' => $course->summary,
422 'summaryformat' => $course->summaryformat,
423 'format' => $course->format,
424 'showgrades' => $course->showgrades,
425 'lang' => clean_param($course->lang, PARAM_LANG),
426 'enablecompletion' => $course->enablecompletion,
427 'completionhascriteria' => $completionhascriteria,
428 'category' => $course->category,
429 'progress' => $progress,
430 'completed' => $completed,
431 'startdate' => $course->startdate,
432 'enddate' => $course->enddate,
433 'marker' => $course->marker,
434 'lastaccess' => $lastaccess,
435 'isfavourite' => isset($favouritecourseids[$course->id]),
436 'hidden' => $hidden,
437 'overviewfiles' => $overviewfiles,
439 if ($returnusercount) {
440 $courseresult['enrolledusercount'] = $enrolledusercount;
442 $result[] = $courseresult;
445 return $result;
449 * Returns description of method result value
451 * @return external_description
453 public static function get_users_courses_returns() {
454 return new external_multiple_structure(
455 new external_single_structure(
456 array(
457 'id' => new external_value(PARAM_INT, 'id of course'),
458 'shortname' => new external_value(PARAM_RAW, 'short name of course'),
459 'fullname' => new external_value(PARAM_RAW, 'long name of course'),
460 'displayname' => new external_value(PARAM_TEXT, 'course display name for lists.', VALUE_OPTIONAL),
461 'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course',
462 VALUE_OPTIONAL),
463 'idnumber' => new external_value(PARAM_RAW, 'id number of course'),
464 'visible' => new external_value(PARAM_INT, '1 means visible, 0 means not yet visible course'),
465 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
466 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
467 'format' => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site', VALUE_OPTIONAL),
468 'showgrades' => new external_value(PARAM_BOOL, 'true if grades are shown, otherwise false', VALUE_OPTIONAL),
469 'lang' => new external_value(PARAM_LANG, 'forced course language', VALUE_OPTIONAL),
470 'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false',
471 VALUE_OPTIONAL),
472 'completionhascriteria' => new external_value(PARAM_BOOL, 'If completion criteria is set.', VALUE_OPTIONAL),
473 'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
474 'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
475 'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL),
476 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
477 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
478 'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL),
479 'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL),
480 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this course a favourite.', VALUE_OPTIONAL),
481 'hidden' => new external_value(PARAM_BOOL, 'If the user hide the course from the dashboard.', VALUE_OPTIONAL),
482 'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL),
489 * Returns description of method parameters value
491 * @return external_description
493 public static function get_potential_users_parameters() {
494 return new external_function_parameters(
495 array(
496 'courseid' => new external_value(PARAM_INT, 'course id'),
497 'enrolid' => new external_value(PARAM_INT, 'enrolment id'),
498 'search' => new external_value(PARAM_RAW, 'query'),
499 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
500 'page' => new external_value(PARAM_INT, 'Page number'),
501 'perpage' => new external_value(PARAM_INT, 'Number per page'),
507 * Get potential users.
509 * @param int $courseid Course id
510 * @param int $enrolid Enrolment id
511 * @param string $search The query
512 * @param boolean $searchanywhere Match anywhere in the string
513 * @param int $page Page number
514 * @param int $perpage Max per page
515 * @return array An array of users
517 public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) {
518 global $PAGE, $DB, $CFG;
520 require_once($CFG->dirroot.'/enrol/locallib.php');
521 require_once($CFG->dirroot.'/user/lib.php');
523 $params = self::validate_parameters(
524 self::get_potential_users_parameters(),
525 array(
526 'courseid' => $courseid,
527 'enrolid' => $enrolid,
528 'search' => $search,
529 'searchanywhere' => $searchanywhere,
530 'page' => $page,
531 'perpage' => $perpage
534 $context = context_course::instance($params['courseid']);
535 try {
536 self::validate_context($context);
537 } catch (Exception $e) {
538 $exceptionparam = new stdClass();
539 $exceptionparam->message = $e->getMessage();
540 $exceptionparam->courseid = $params['courseid'];
541 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
543 require_capability('moodle/course:enrolreview', $context);
545 $course = $DB->get_record('course', array('id' => $params['courseid']));
546 $manager = new course_enrolment_manager($PAGE, $course);
548 $users = $manager->get_potential_users($params['enrolid'],
549 $params['search'],
550 $params['searchanywhere'],
551 $params['page'],
552 $params['perpage']);
554 $results = array();
555 // Add also extra user fields.
556 $requiredfields = array_merge(
557 ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
558 get_extra_user_fields($context)
560 foreach ($users['users'] as $id => $user) {
561 // Note: We pass the course here to validate that the current user can at least view user details in this course.
562 // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
563 // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
564 // there is no way to find users who aren't in the course in order to enrol them.
565 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
566 $results[] = $userdetails;
569 return $results;
573 * Returns description of method result value
575 * @return external_description
577 public static function get_potential_users_returns() {
578 global $CFG;
579 require_once($CFG->dirroot . '/user/externallib.php');
580 return new external_multiple_structure(core_user_external::user_description());
584 * Returns description of method parameters
586 * @return external_function_parameters
588 public static function get_enrolled_users_parameters() {
589 return new external_function_parameters(
590 array(
591 'courseid' => new external_value(PARAM_INT, 'course id'),
592 'options' => new external_multiple_structure(
593 new external_single_structure(
594 array(
595 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
596 'value' => new external_value(PARAM_RAW, 'option value')
598 ), 'Option names:
599 * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
600 * groupid (integer) return only users in this group id. If the course has groups enabled and this param
601 isn\'t defined, returns all the viewable users.
602 This option requires \'moodle/site:accessallgroups\' on the course context if the
603 user doesn\'t belong to the group.
604 * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
605 * userfields (\'string, string, ...\') return only the values of these user fields.
606 * limitfrom (integer) sql limit from.
607 * limitnumber (integer) maximum number of returned users.
608 * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
609 * sortdirection (string) ASC or DESC',
610 VALUE_DEFAULT, array()),
616 * Get course participants details
618 * @param int $courseid course id
619 * @param array $options options {
620 * 'name' => option name
621 * 'value' => option value
623 * @return array An array of users
625 public static function get_enrolled_users($courseid, $options = array()) {
626 global $CFG, $USER, $DB;
628 require_once($CFG->dirroot . '/course/lib.php');
629 require_once($CFG->dirroot . "/user/lib.php");
631 $params = self::validate_parameters(
632 self::get_enrolled_users_parameters(),
633 array(
634 'courseid'=>$courseid,
635 'options'=>$options
638 $withcapability = '';
639 $groupid = 0;
640 $onlyactive = false;
641 $userfields = array();
642 $limitfrom = 0;
643 $limitnumber = 0;
644 $sortby = 'us.id';
645 $sortparams = array();
646 $sortdirection = 'ASC';
647 foreach ($options as $option) {
648 switch ($option['name']) {
649 case 'withcapability':
650 $withcapability = $option['value'];
651 break;
652 case 'groupid':
653 $groupid = (int)$option['value'];
654 break;
655 case 'onlyactive':
656 $onlyactive = !empty($option['value']);
657 break;
658 case 'userfields':
659 $thefields = explode(',', $option['value']);
660 foreach ($thefields as $f) {
661 $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
663 break;
664 case 'limitfrom' :
665 $limitfrom = clean_param($option['value'], PARAM_INT);
666 break;
667 case 'limitnumber' :
668 $limitnumber = clean_param($option['value'], PARAM_INT);
669 break;
670 case 'sortby':
671 $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
672 if (!in_array($option['value'], $sortallowedvalues)) {
673 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
674 'allowed values are: ' . implode(',', $sortallowedvalues));
676 if ($option['value'] == 'siteorder') {
677 list($sortby, $sortparams) = users_order_by_sql('us');
678 } else {
679 $sortby = 'us.' . $option['value'];
681 break;
682 case 'sortdirection':
683 $sortdirection = strtoupper($option['value']);
684 $directionallowedvalues = array('ASC', 'DESC');
685 if (!in_array($sortdirection, $directionallowedvalues)) {
686 throw new invalid_parameter_exception('Invalid value for sortdirection parameter
687 (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
689 break;
693 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
694 $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
695 if ($courseid == SITEID) {
696 $context = context_system::instance();
697 } else {
698 $context = $coursecontext;
700 try {
701 self::validate_context($context);
702 } catch (Exception $e) {
703 $exceptionparam = new stdClass();
704 $exceptionparam->message = $e->getMessage();
705 $exceptionparam->courseid = $params['courseid'];
706 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
709 course_require_view_participants($context);
711 // to overwrite this parameter, you need role:review capability
712 if ($withcapability) {
713 require_capability('moodle/role:review', $coursecontext);
715 // need accessallgroups capability if you want to overwrite this option
716 if (!empty($groupid) && !groups_is_member($groupid)) {
717 require_capability('moodle/site:accessallgroups', $coursecontext);
719 // to overwrite this option, you need course:enrolereview permission
720 if ($onlyactive) {
721 require_capability('moodle/course:enrolreview', $coursecontext);
724 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
725 $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
726 $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
727 $enrolledparams['contextlevel'] = CONTEXT_USER;
729 $groupjoin = '';
730 if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
731 !has_capability('moodle/site:accessallgroups', $coursecontext)) {
732 // Filter by groups the user can view.
733 $usergroups = groups_get_user_groups($course->id);
734 if (!empty($usergroups['0'])) {
735 list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
736 $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
737 $enrolledparams = array_merge($enrolledparams, $groupparams);
738 } else {
739 // User doesn't belong to any group, so he can't see any user. Return an empty array.
740 return array();
743 $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
744 FROM {user} us
745 JOIN (
746 SELECT DISTINCT u.id $ctxselect
747 FROM {user} u $ctxjoin $groupjoin
748 WHERE u.id IN ($enrolledsql)
749 ) q ON q.id = us.id
750 LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
751 ORDER BY $sortby $sortdirection";
752 $enrolledparams = array_merge($enrolledparams, $sortparams);
753 $enrolledparams['courseid'] = $courseid;
755 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
756 $users = array();
757 foreach ($enrolledusers as $user) {
758 context_helper::preload_from_record($user);
759 if ($userdetails = user_get_user_details($user, $course, $userfields)) {
760 $users[] = $userdetails;
763 $enrolledusers->close();
765 return $users;
769 * Returns description of method result value
771 * @return external_description
773 public static function get_enrolled_users_returns() {
774 return new external_multiple_structure(
775 new external_single_structure(
776 array(
777 'id' => new external_value(PARAM_INT, 'ID of the user'),
778 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
779 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
780 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
781 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
782 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
783 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
784 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
785 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
786 'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
787 'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
788 'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
789 'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
790 'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
791 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
792 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
793 'idnumber' => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
794 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
795 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
796 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
797 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
798 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
799 'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
800 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
801 'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
802 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
803 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
804 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
805 'customfields' => new external_multiple_structure(
806 new external_single_structure(
807 array(
808 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
809 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
810 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
811 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
813 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
814 'groups' => new external_multiple_structure(
815 new external_single_structure(
816 array(
817 'id' => new external_value(PARAM_INT, 'group id'),
818 'name' => new external_value(PARAM_RAW, 'group name'),
819 'description' => new external_value(PARAM_RAW, 'group description'),
820 'descriptionformat' => new external_format_value('description'),
822 ), 'user groups', VALUE_OPTIONAL),
823 'roles' => new external_multiple_structure(
824 new external_single_structure(
825 array(
826 'roleid' => new external_value(PARAM_INT, 'role id'),
827 'name' => new external_value(PARAM_RAW, 'role name'),
828 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
829 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
831 ), 'user roles', VALUE_OPTIONAL),
832 'preferences' => new external_multiple_structure(
833 new external_single_structure(
834 array(
835 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
836 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
838 ), 'User preferences', VALUE_OPTIONAL),
839 'enrolledcourses' => new external_multiple_structure(
840 new external_single_structure(
841 array(
842 'id' => new external_value(PARAM_INT, 'Id of the course'),
843 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
844 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
846 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
853 * Returns description of get_course_enrolment_methods() parameters
855 * @return external_function_parameters
857 public static function get_course_enrolment_methods_parameters() {
858 return new external_function_parameters(
859 array(
860 'courseid' => new external_value(PARAM_INT, 'Course id')
866 * Get list of active course enrolment methods for current user.
868 * @param int $courseid
869 * @return array of course enrolment methods
870 * @throws moodle_exception
872 public static function get_course_enrolment_methods($courseid) {
873 global $DB;
875 $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
876 self::validate_context(context_system::instance());
878 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
879 if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
880 throw new moodle_exception('coursehidden');
883 $result = array();
884 $enrolinstances = enrol_get_instances($params['courseid'], true);
885 foreach ($enrolinstances as $enrolinstance) {
886 if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
887 if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
888 $result[] = (array) $instanceinfo;
892 return $result;
896 * Returns description of get_course_enrolment_methods() result value
898 * @return external_description
900 public static function get_course_enrolment_methods_returns() {
901 return new external_multiple_structure(
902 new external_single_structure(
903 array(
904 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
905 'courseid' => new external_value(PARAM_INT, 'id of course'),
906 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
907 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
908 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
909 'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
916 * Returns description of edit_user_enrolment() parameters
918 * @return external_function_parameters
920 public static function edit_user_enrolment_parameters() {
921 return new external_function_parameters(
922 array(
923 'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
924 'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
925 'status' => new external_value(PARAM_INT, 'Enrolment status'),
926 'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
927 'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
933 * External function that updates a given user enrolment.
935 * @param int $courseid The course ID.
936 * @param int $ueid The user enrolment ID.
937 * @param int $status The enrolment status.
938 * @param int $timestart Enrolment start timestamp.
939 * @param int $timeend Enrolment end timestamp.
940 * @return array An array consisting of the processing result, errors and form output, if available.
942 public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
943 global $CFG, $DB, $PAGE;
945 $params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
946 'courseid' => $courseid,
947 'ueid' => $ueid,
948 'status' => $status,
949 'timestart' => $timestart,
950 'timeend' => $timeend,
953 $course = get_course($courseid);
954 $context = context_course::instance($course->id);
955 self::validate_context($context);
957 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
958 $userenroldata = [
959 'status' => $params['status'],
960 'timestart' => $params['timestart'],
961 'timeend' => $params['timeend'],
964 $result = false;
965 $errors = [];
967 // Validate data against the edit user enrolment form.
968 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
969 $plugin = enrol_get_plugin($instance->enrol);
970 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
971 $customformdata = [
972 'ue' => $userenrolment,
973 'modal' => true,
974 'enrolinstancename' => $plugin->get_instance_name($instance)
976 $mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
977 $mform->set_data($userenroldata);
978 $validationerrors = $mform->validation($userenroldata, null);
979 if (empty($validationerrors)) {
980 require_once($CFG->dirroot . '/enrol/locallib.php');
981 $manager = new course_enrolment_manager($PAGE, $course);
982 $result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
983 } else {
984 foreach ($validationerrors as $key => $errormessage) {
985 $errors[] = (object)[
986 'key' => $key,
987 'message' => $errormessage
992 return [
993 'result' => $result,
994 'errors' => $errors,
999 * Returns description of edit_user_enrolment() result value
1001 * @return external_description
1003 public static function edit_user_enrolment_returns() {
1004 return new external_single_structure(
1005 array(
1006 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1007 'errors' => new external_multiple_structure(
1008 new external_single_structure(
1009 array(
1010 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1011 'message' => new external_value(PARAM_TEXT, 'The error message'),
1013 ), 'List of validation errors'
1020 * Returns description of unenrol_user_enrolment() parameters
1022 * @return external_function_parameters
1024 public static function unenrol_user_enrolment_parameters() {
1025 return new external_function_parameters(
1026 array(
1027 'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
1033 * External function that unenrols a given user enrolment.
1035 * @param int $ueid The user enrolment ID.
1036 * @return array An array consisting of the processing result, errors.
1038 public static function unenrol_user_enrolment($ueid) {
1039 global $CFG, $DB, $PAGE;
1041 $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1042 'ueid' => $ueid
1045 $result = false;
1046 $errors = [];
1048 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
1049 if ($userenrolment) {
1050 $userid = $userenrolment->userid;
1051 $enrolid = $userenrolment->enrolid;
1052 $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
1053 $courseid = $enrol->courseid;
1054 $course = get_course($courseid);
1055 $context = context_course::instance($course->id);
1056 self::validate_context($context);
1057 } else {
1058 $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
1061 // If the userenrolment exists, unenrol the user.
1062 if (!isset($validationerrors)) {
1063 require_once($CFG->dirroot . '/enrol/locallib.php');
1064 $manager = new course_enrolment_manager($PAGE, $course);
1065 $result = $manager->unenrol_user($userenrolment);
1066 } else {
1067 foreach ($validationerrors as $key => $errormessage) {
1068 $errors[] = (object)[
1069 'key' => $key,
1070 'message' => $errormessage
1075 return [
1076 'result' => $result,
1077 'errors' => $errors,
1082 * Returns description of unenrol_user_enrolment() result value
1084 * @return external_description
1086 public static function unenrol_user_enrolment_returns() {
1087 return new external_single_structure(
1088 array(
1089 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1090 'errors' => new external_multiple_structure(
1091 new external_single_structure(
1092 array(
1093 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1094 'message' => new external_value(PARAM_TEXT, 'The error message'),
1096 ), 'List of validation errors'
1104 * Role external functions
1106 * @package core_role
1107 * @category external
1108 * @copyright 2011 Jerome Mouneyrac
1109 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1110 * @since Moodle 2.2
1112 class core_role_external extends external_api {
1115 * Returns description of method parameters
1117 * @return external_function_parameters
1119 public static function assign_roles_parameters() {
1120 return new external_function_parameters(
1121 array(
1122 'assignments' => new external_multiple_structure(
1123 new external_single_structure(
1124 array(
1125 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1126 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1127 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1128 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1129 (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1130 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
1139 * Manual role assignments to users
1141 * @param array $assignments An array of manual role assignment
1143 public static function assign_roles($assignments) {
1144 global $DB;
1146 // Do basic automatic PARAM checks on incoming data, using params description
1147 // If any problems are found then exceptions are thrown with helpful error messages
1148 $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1150 $transaction = $DB->start_delegated_transaction();
1152 foreach ($params['assignments'] as $assignment) {
1153 // Ensure correct context level with a instance id or contextid is passed.
1154 $context = self::get_context_from_params($assignment);
1156 // Ensure the current user is allowed to run this function in the enrolment context.
1157 self::validate_context($context);
1158 require_capability('moodle/role:assign', $context);
1160 // throw an exception if user is not able to assign the role in this context
1161 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1163 if (!array_key_exists($assignment['roleid'], $roles)) {
1164 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1167 role_assign($assignment['roleid'], $assignment['userid'], $context->id);
1170 $transaction->allow_commit();
1174 * Returns description of method result value
1176 * @return null
1178 public static function assign_roles_returns() {
1179 return null;
1184 * Returns description of method parameters
1186 * @return external_function_parameters
1188 public static function unassign_roles_parameters() {
1189 return new external_function_parameters(
1190 array(
1191 'unassignments' => new external_multiple_structure(
1192 new external_single_structure(
1193 array(
1194 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1195 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1196 'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1197 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1198 + (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1199 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
1208 * Unassign roles from users
1210 * @param array $unassignments An array of unassignment
1212 public static function unassign_roles($unassignments) {
1213 global $DB;
1215 // Do basic automatic PARAM checks on incoming data, using params description
1216 // If any problems are found then exceptions are thrown with helpful error messages
1217 $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1219 $transaction = $DB->start_delegated_transaction();
1221 foreach ($params['unassignments'] as $unassignment) {
1222 // Ensure the current user is allowed to run this function in the unassignment context
1223 $context = self::get_context_from_params($unassignment);
1224 self::validate_context($context);
1225 require_capability('moodle/role:assign', $context);
1227 // throw an exception if user is not able to unassign the role in this context
1228 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1229 if (!array_key_exists($unassignment['roleid'], $roles)) {
1230 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1233 role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
1236 $transaction->allow_commit();
1240 * Returns description of method result value
1242 * @return null
1244 public static function unassign_roles_returns() {
1245 return null;