MDL-77204 mod_wiki: Clean filename in Moodle1 backup
[moodle.git] / enrol / externallib.php
blobafe0e83944f306b75629b82deab1e77725589ff3
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 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
215 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
216 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
217 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
218 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
219 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
220 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
221 'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
222 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
223 'country' => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
224 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
225 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
226 'customfields' => new external_multiple_structure(
227 new external_single_structure(
228 array(
229 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
230 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
231 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
232 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
234 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
235 'groups' => new external_multiple_structure(
236 new external_single_structure(
237 array(
238 'id' => new external_value(PARAM_INT, 'group id'),
239 'name' => new external_value(PARAM_RAW, 'group name'),
240 'description' => new external_value(PARAM_RAW, 'group description'),
242 ), 'user groups', VALUE_OPTIONAL),
243 'roles' => new external_multiple_structure(
244 new external_single_structure(
245 array(
246 'roleid' => new external_value(PARAM_INT, 'role id'),
247 'name' => new external_value(PARAM_RAW, 'role name'),
248 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
249 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
251 ), 'user roles', VALUE_OPTIONAL),
252 'preferences' => new external_multiple_structure(
253 new external_single_structure(
254 array(
255 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
256 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
258 ), 'User preferences', VALUE_OPTIONAL),
259 'enrolledcourses' => new external_multiple_structure(
260 new external_single_structure(
261 array(
262 'id' => new external_value(PARAM_INT, 'Id of the course'),
263 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
264 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
266 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
268 ), 'List of users that are enrolled in the course and have the specified capability'),
275 * Returns description of method parameters
277 * @return external_function_parameters
279 public static function get_users_courses_parameters() {
280 return new external_function_parameters(
281 array(
282 'userid' => new external_value(PARAM_INT, 'user id'),
283 'returnusercount' => new external_value(PARAM_BOOL,
284 'Include count of enrolled users for each course? This can add several seconds to the response time'
285 . ' if a user is on several large courses, so set this to false if the value will not be used to'
286 . ' improve performance.',
287 VALUE_DEFAULT, true),
293 * Get list of courses user is enrolled in (only active enrolments are returned).
294 * Please note the current user must be able to access the course, otherwise the course is not included.
296 * @param int $userid
297 * @param bool $returnusercount
298 * @return array of courses
300 public static function get_users_courses($userid, $returnusercount = true) {
301 global $CFG, $USER, $DB;
303 require_once($CFG->dirroot . '/course/lib.php');
304 require_once($CFG->dirroot . '/user/lib.php');
305 require_once($CFG->libdir . '/completionlib.php');
307 // Do basic automatic PARAM checks on incoming data, using params description
308 // If any problems are found then exceptions are thrown with helpful error messages
309 $params = self::validate_parameters(self::get_users_courses_parameters(),
310 ['userid' => $userid, 'returnusercount' => $returnusercount]);
311 $userid = $params['userid'];
312 $returnusercount = $params['returnusercount'];
314 $courses = enrol_get_users_courses($userid, true, '*');
315 $result = array();
317 // Get user data including last access to courses.
318 $user = get_complete_user_data('id', $userid);
319 $sameuser = $USER->id == $userid;
321 // Retrieve favourited courses (starred).
322 $favouritecourseids = array();
323 if ($sameuser) {
324 $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
325 $favourites = $ufservice->find_favourites_by_type('core_course', 'courses');
327 if ($favourites) {
328 $favouritecourseids = array_flip(array_map(
329 function($favourite) {
330 return $favourite->itemid;
331 }, $favourites));
335 foreach ($courses as $course) {
336 $context = context_course::instance($course->id, IGNORE_MISSING);
337 try {
338 self::validate_context($context);
339 } catch (Exception $e) {
340 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
341 continue;
344 // If viewing details of another user, then we must be able to view participants as well as profile of that user.
345 if (!$sameuser && (!course_can_view_participants($context) || !user_can_view_profile($user, $course))) {
346 continue;
349 if ($returnusercount) {
350 list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
351 $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids";
352 $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
355 $displayname = external_format_string(get_course_display_name_for_list($course), $context->id);
356 list($course->summary, $course->summaryformat) =
357 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
358 $course->fullname = external_format_string($course->fullname, $context->id);
359 $course->shortname = external_format_string($course->shortname, $context->id);
361 $progress = null;
362 $completed = null;
363 $completionhascriteria = false;
364 $completionusertracked = false;
366 // Return only private information if the user should be able to see it.
367 if ($sameuser || completion_can_view_data($userid, $course)) {
368 if ($course->enablecompletion) {
369 $completion = new completion_info($course);
370 $completed = $completion->is_course_complete($userid);
371 $completionhascriteria = $completion->has_criteria();
372 $completionusertracked = $completion->is_tracked_user($userid);
373 $progress = \core_completion\progress::get_course_progress_percentage($course, $userid);
377 $lastaccess = null;
378 // Check if last access is a hidden field.
379 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
380 $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']);
381 if (!$canviewlastaccess) {
382 $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context);
385 if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) {
386 $lastaccess = $user->lastcourseaccess[$course->id];
389 $hidden = false;
390 if ($sameuser) {
391 $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0));
394 // Retrieve course overview used files.
395 $courselist = new core_course_list_element($course);
396 $overviewfiles = array();
397 foreach ($courselist->get_course_overviewfiles() as $file) {
398 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
399 $file->get_filearea(), null, $file->get_filepath(),
400 $file->get_filename())->out(false);
401 $overviewfiles[] = array(
402 'filename' => $file->get_filename(),
403 'fileurl' => $fileurl,
404 'filesize' => $file->get_filesize(),
405 'filepath' => $file->get_filepath(),
406 'mimetype' => $file->get_mimetype(),
407 'timemodified' => $file->get_timemodified(),
411 $courseresult = [
412 'id' => $course->id,
413 'shortname' => $course->shortname,
414 'fullname' => $course->fullname,
415 'displayname' => $displayname,
416 'idnumber' => $course->idnumber,
417 'visible' => $course->visible,
418 'summary' => $course->summary,
419 'summaryformat' => $course->summaryformat,
420 'format' => $course->format,
421 'showgrades' => $course->showgrades,
422 'lang' => clean_param($course->lang, PARAM_LANG),
423 'enablecompletion' => $course->enablecompletion,
424 'completionhascriteria' => $completionhascriteria,
425 'completionusertracked' => $completionusertracked,
426 'category' => $course->category,
427 'progress' => $progress,
428 'completed' => $completed,
429 'startdate' => $course->startdate,
430 'enddate' => $course->enddate,
431 'marker' => $course->marker,
432 'lastaccess' => $lastaccess,
433 'isfavourite' => isset($favouritecourseids[$course->id]),
434 'hidden' => $hidden,
435 'overviewfiles' => $overviewfiles,
436 'showactivitydates' => $course->showactivitydates,
437 'showcompletionconditions' => $course->showcompletionconditions,
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_RAW, '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 'completionusertracked' => new external_value(PARAM_BOOL, 'If the user is completion tracked.', VALUE_OPTIONAL),
474 'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
475 'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
476 'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL),
477 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
478 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
479 'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL),
480 'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL),
481 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this course a favourite.', VALUE_OPTIONAL),
482 'hidden' => new external_value(PARAM_BOOL, 'If the user hide the course from the dashboard.', VALUE_OPTIONAL),
483 'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL),
484 'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'),
485 'showcompletionconditions' => new external_value(PARAM_BOOL, 'Whether the activity completion conditions are shown or not'),
492 * Returns description of method parameters value
494 * @return external_description
496 public static function get_potential_users_parameters() {
497 return new external_function_parameters(
498 array(
499 'courseid' => new external_value(PARAM_INT, 'course id'),
500 'enrolid' => new external_value(PARAM_INT, 'enrolment id'),
501 'search' => new external_value(PARAM_RAW, 'query'),
502 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
503 'page' => new external_value(PARAM_INT, 'Page number'),
504 'perpage' => new external_value(PARAM_INT, 'Number per page'),
510 * Get potential users.
512 * @param int $courseid Course id
513 * @param int $enrolid Enrolment id
514 * @param string $search The query
515 * @param boolean $searchanywhere Match anywhere in the string
516 * @param int $page Page number
517 * @param int $perpage Max per page
518 * @return array An array of users
520 public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) {
521 global $PAGE, $DB, $CFG;
523 require_once($CFG->dirroot.'/enrol/locallib.php');
524 require_once($CFG->dirroot.'/user/lib.php');
526 $params = self::validate_parameters(
527 self::get_potential_users_parameters(),
528 array(
529 'courseid' => $courseid,
530 'enrolid' => $enrolid,
531 'search' => $search,
532 'searchanywhere' => $searchanywhere,
533 'page' => $page,
534 'perpage' => $perpage
537 $context = context_course::instance($params['courseid']);
538 try {
539 self::validate_context($context);
540 } catch (Exception $e) {
541 $exceptionparam = new stdClass();
542 $exceptionparam->message = $e->getMessage();
543 $exceptionparam->courseid = $params['courseid'];
544 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
546 require_capability('moodle/course:enrolreview', $context);
548 $course = $DB->get_record('course', array('id' => $params['courseid']));
549 $manager = new course_enrolment_manager($PAGE, $course);
551 $users = $manager->get_potential_users($params['enrolid'],
552 $params['search'],
553 $params['searchanywhere'],
554 $params['page'],
555 $params['perpage']);
557 $results = array();
558 // Add also extra user fields.
559 $identityfields = \core_user\fields::get_identity_fields($context, true);
560 $customprofilefields = [];
561 foreach ($identityfields as $key => $value) {
562 if ($fieldname = \core_user\fields::match_custom_field($value)) {
563 unset($identityfields[$key]);
564 $customprofilefields[$fieldname] = true;
567 if ($customprofilefields) {
568 $identityfields[] = 'customfields';
570 $requiredfields = array_merge(
571 ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
572 $identityfields
574 foreach ($users['users'] as $id => $user) {
575 // Note: We pass the course here to validate that the current user can at least view user details in this course.
576 // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
577 // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
578 // there is no way to find users who aren't in the course in order to enrol them.
579 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
580 // For custom fields, only return the ones we actually need.
581 if ($customprofilefields && array_key_exists('customfields', $userdetails)) {
582 foreach ($userdetails['customfields'] as $key => $data) {
583 if (!array_key_exists($data['shortname'], $customprofilefields)) {
584 unset($userdetails['customfields'][$key]);
587 $userdetails['customfields'] = array_values($userdetails['customfields']);
589 $results[] = $userdetails;
592 return $results;
596 * Returns description of method result value
598 * @return external_description
600 public static function get_potential_users_returns() {
601 global $CFG;
602 require_once($CFG->dirroot . '/user/externallib.php');
603 return new external_multiple_structure(core_user_external::user_description());
607 * Returns description of method parameters
609 * @return external_function_parameters
611 public static function search_users_parameters(): external_function_parameters {
612 return new external_function_parameters(
614 'courseid' => new external_value(PARAM_INT, 'course id'),
615 'search' => new external_value(PARAM_RAW, 'query'),
616 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
617 'page' => new external_value(PARAM_INT, 'Page number'),
618 'perpage' => new external_value(PARAM_INT, 'Number per page'),
624 * Search course participants.
626 * @param int $courseid Course id
627 * @param string $search The query
628 * @param bool $searchanywhere Match anywhere in the string
629 * @param int $page Page number
630 * @param int $perpage Max per page
631 * @return array An array of users
632 * @throws moodle_exception
634 public static function search_users(int $courseid, string $search, bool $searchanywhere, int $page, int $perpage): array {
635 global $PAGE, $DB, $CFG;
637 require_once($CFG->dirroot.'/enrol/locallib.php');
638 require_once($CFG->dirroot.'/user/lib.php');
640 $params = self::validate_parameters(
641 self::search_users_parameters(),
643 'courseid' => $courseid,
644 'search' => $search,
645 'searchanywhere' => $searchanywhere,
646 'page' => $page,
647 'perpage' => $perpage
650 $context = context_course::instance($params['courseid']);
651 try {
652 self::validate_context($context);
653 } catch (Exception $e) {
654 $exceptionparam = new stdClass();
655 $exceptionparam->message = $e->getMessage();
656 $exceptionparam->courseid = $params['courseid'];
657 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
659 course_require_view_participants($context);
661 $course = get_course($params['courseid']);
662 $manager = new course_enrolment_manager($PAGE, $course);
664 $users = $manager->search_users($params['search'],
665 $params['searchanywhere'],
666 $params['page'],
667 $params['perpage']);
669 $results = [];
670 // Add also extra user fields.
671 $requiredfields = array_merge(
672 ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
673 // TODO Does not support custom user profile fields (MDL-70456).
674 \core_user\fields::get_identity_fields($context, false)
676 foreach ($users['users'] as $user) {
677 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
678 $results[] = $userdetails;
681 return $results;
685 * Returns description of method result value
687 * @return external_multiple_structure
689 public static function search_users_returns(): external_multiple_structure {
690 global $CFG;
691 require_once($CFG->dirroot . '/user/externallib.php');
692 return new external_multiple_structure(core_user_external::user_description());
696 * Returns description of method parameters
698 * @return external_function_parameters
700 public static function get_enrolled_users_parameters() {
701 return new external_function_parameters(
703 'courseid' => new external_value(PARAM_INT, 'course id'),
704 'options' => new external_multiple_structure(
705 new external_single_structure(
707 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
708 'value' => new external_value(PARAM_RAW, 'option value')
710 ), 'Option names:
711 * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
712 * groupid (integer) return only users in this group id. If the course has groups enabled and this param
713 isn\'t defined, returns all the viewable users.
714 This option requires \'moodle/site:accessallgroups\' on the course context if the
715 user doesn\'t belong to the group.
716 * onlyactive (integer) return only users with active enrolments and matching time restrictions.
717 This option requires \'moodle/course:enrolreview\' on the course context.
718 Please note that this option can\'t
719 be used together with onlysuspended (only one can be active).
720 * onlysuspended (integer) return only suspended users. This option requires
721 \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
722 be used together with onlyactive (only one can be active).
723 * userfields (\'string, string, ...\') return only the values of these user fields.
724 * limitfrom (integer) sql limit from.
725 * limitnumber (integer) maximum number of returned users.
726 * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
727 * sortdirection (string) ASC or DESC',
728 VALUE_DEFAULT, []),
734 * Get course participants details
736 * @param int $courseid course id
737 * @param array $options options {
738 * 'name' => option name
739 * 'value' => option value
741 * @return array An array of users
743 public static function get_enrolled_users($courseid, $options = []) {
744 global $CFG, $USER, $DB;
746 require_once($CFG->dirroot . '/course/lib.php');
747 require_once($CFG->dirroot . "/user/lib.php");
749 $params = self::validate_parameters(
750 self::get_enrolled_users_parameters(),
752 'courseid'=>$courseid,
753 'options'=>$options
756 $withcapability = '';
757 $groupid = 0;
758 $onlyactive = false;
759 $onlysuspended = false;
760 $userfields = [];
761 $limitfrom = 0;
762 $limitnumber = 0;
763 $sortby = 'us.id';
764 $sortparams = [];
765 $sortdirection = 'ASC';
766 foreach ($options as $option) {
767 switch ($option['name']) {
768 case 'withcapability':
769 $withcapability = $option['value'];
770 break;
771 case 'groupid':
772 $groupid = (int)$option['value'];
773 break;
774 case 'onlyactive':
775 $onlyactive = !empty($option['value']);
776 break;
777 case 'onlysuspended':
778 $onlysuspended = !empty($option['value']);
779 break;
780 case 'userfields':
781 $thefields = explode(',', $option['value']);
782 foreach ($thefields as $f) {
783 $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
785 break;
786 case 'limitfrom' :
787 $limitfrom = clean_param($option['value'], PARAM_INT);
788 break;
789 case 'limitnumber' :
790 $limitnumber = clean_param($option['value'], PARAM_INT);
791 break;
792 case 'sortby':
793 $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
794 if (!in_array($option['value'], $sortallowedvalues)) {
795 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
796 $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
798 if ($option['value'] == 'siteorder') {
799 list($sortby, $sortparams) = users_order_by_sql('us');
800 } else {
801 $sortby = 'us.' . $option['value'];
803 break;
804 case 'sortdirection':
805 $sortdirection = strtoupper($option['value']);
806 $directionallowedvalues = ['ASC', 'DESC'];
807 if (!in_array($sortdirection, $directionallowedvalues)) {
808 throw new invalid_parameter_exception('Invalid value for sortdirection parameter
809 (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
811 break;
815 $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
816 $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
817 if ($courseid == SITEID) {
818 $context = context_system::instance();
819 } else {
820 $context = $coursecontext;
822 try {
823 self::validate_context($context);
824 } catch (Exception $e) {
825 $exceptionparam = new stdClass();
826 $exceptionparam->message = $e->getMessage();
827 $exceptionparam->courseid = $params['courseid'];
828 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
831 course_require_view_participants($context);
833 // to overwrite this parameter, you need role:review capability
834 if ($withcapability) {
835 require_capability('moodle/role:review', $coursecontext);
837 // need accessallgroups capability if you want to overwrite this option
838 if (!empty($groupid) && !groups_is_member($groupid)) {
839 require_capability('moodle/site:accessallgroups', $coursecontext);
841 // to overwrite this option, you need course:enrolereview permission
842 if ($onlyactive || $onlysuspended) {
843 require_capability('moodle/course:enrolreview', $coursecontext);
846 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
847 $onlysuspended);
848 $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
849 $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
850 $enrolledparams['contextlevel'] = CONTEXT_USER;
852 $groupjoin = '';
853 if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
854 !has_capability('moodle/site:accessallgroups', $coursecontext)) {
855 // Filter by groups the user can view.
856 $usergroups = groups_get_user_groups($course->id);
857 if (!empty($usergroups['0'])) {
858 list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
859 $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
860 $enrolledparams = array_merge($enrolledparams, $groupparams);
861 } else {
862 // User doesn't belong to any group, so he can't see any user. Return an empty array.
863 return [];
866 $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
867 FROM {user} us
868 JOIN (
869 SELECT DISTINCT u.id $ctxselect
870 FROM {user} u $ctxjoin $groupjoin
871 WHERE u.id IN ($enrolledsql)
872 ) q ON q.id = us.id
873 LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
874 ORDER BY $sortby $sortdirection";
875 $enrolledparams = array_merge($enrolledparams, $sortparams);
876 $enrolledparams['courseid'] = $courseid;
878 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
879 $users = [];
880 foreach ($enrolledusers as $user) {
881 context_helper::preload_from_record($user);
882 if ($userdetails = user_get_user_details($user, $course, $userfields)) {
883 $users[] = $userdetails;
886 $enrolledusers->close();
888 return $users;
892 * Returns description of method result value
894 * @return external_description
896 public static function get_enrolled_users_returns() {
897 return new external_multiple_structure(
898 new external_single_structure(
900 'id' => new external_value(PARAM_INT, 'ID of the user'),
901 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
902 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
903 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
904 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
905 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
906 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
907 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
908 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
909 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
910 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
911 'idnumber' => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
912 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
913 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
914 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
915 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
916 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
917 'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
918 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
919 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
920 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
921 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
922 'customfields' => new external_multiple_structure(
923 new external_single_structure(
925 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
926 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
927 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
928 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
930 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
931 'groups' => new external_multiple_structure(
932 new external_single_structure(
934 'id' => new external_value(PARAM_INT, 'group id'),
935 'name' => new external_value(PARAM_RAW, 'group name'),
936 'description' => new external_value(PARAM_RAW, 'group description'),
937 'descriptionformat' => new external_format_value('description'),
939 ), 'user groups', VALUE_OPTIONAL),
940 'roles' => new external_multiple_structure(
941 new external_single_structure(
943 'roleid' => new external_value(PARAM_INT, 'role id'),
944 'name' => new external_value(PARAM_RAW, 'role name'),
945 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
946 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
948 ), 'user roles', VALUE_OPTIONAL),
949 'preferences' => new external_multiple_structure(
950 new external_single_structure(
952 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
953 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
955 ), 'User preferences', VALUE_OPTIONAL),
956 'enrolledcourses' => new external_multiple_structure(
957 new external_single_structure(
959 'id' => new external_value(PARAM_INT, 'Id of the course'),
960 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
961 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
963 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
970 * Returns description of get_course_enrolment_methods() parameters
972 * @return external_function_parameters
974 public static function get_course_enrolment_methods_parameters() {
975 return new external_function_parameters(
976 array(
977 'courseid' => new external_value(PARAM_INT, 'Course id')
983 * Get list of active course enrolment methods for current user.
985 * @param int $courseid
986 * @return array of course enrolment methods
987 * @throws moodle_exception
989 public static function get_course_enrolment_methods($courseid) {
990 global $DB;
992 $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
993 self::validate_context(context_system::instance());
995 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
996 if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
997 throw new moodle_exception('coursehidden');
1000 $result = array();
1001 $enrolinstances = enrol_get_instances($params['courseid'], true);
1002 foreach ($enrolinstances as $enrolinstance) {
1003 if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
1004 if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
1005 $result[] = (array) $instanceinfo;
1009 return $result;
1013 * Returns description of get_course_enrolment_methods() result value
1015 * @return external_description
1017 public static function get_course_enrolment_methods_returns() {
1018 return new external_multiple_structure(
1019 new external_single_structure(
1020 array(
1021 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
1022 'courseid' => new external_value(PARAM_INT, 'id of course'),
1023 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
1024 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
1025 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
1026 'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
1033 * Returns description of edit_user_enrolment() parameters
1035 * @deprecated since 3.8
1036 * @return external_function_parameters
1038 public static function edit_user_enrolment_parameters() {
1039 return new external_function_parameters(
1040 array(
1041 'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
1042 'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
1043 'status' => new external_value(PARAM_INT, 'Enrolment status'),
1044 'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
1045 'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
1051 * External function that updates a given user enrolment.
1053 * @deprecated since 3.8
1054 * @param int $courseid The course ID.
1055 * @param int $ueid The user enrolment ID.
1056 * @param int $status The enrolment status.
1057 * @param int $timestart Enrolment start timestamp.
1058 * @param int $timeend Enrolment end timestamp.
1059 * @return array An array consisting of the processing result, errors and form output, if available.
1061 public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
1062 global $CFG, $DB, $PAGE;
1064 $params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
1065 'courseid' => $courseid,
1066 'ueid' => $ueid,
1067 'status' => $status,
1068 'timestart' => $timestart,
1069 'timeend' => $timeend,
1072 $course = get_course($courseid);
1073 $context = context_course::instance($course->id);
1074 self::validate_context($context);
1076 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
1077 $userenroldata = [
1078 'status' => $params['status'],
1079 'timestart' => $params['timestart'],
1080 'timeend' => $params['timeend'],
1083 $result = false;
1084 $errors = [];
1086 // Validate data against the edit user enrolment form.
1087 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1088 $plugin = enrol_get_plugin($instance->enrol);
1089 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1090 $customformdata = [
1091 'ue' => $userenrolment,
1092 'modal' => true,
1093 'enrolinstancename' => $plugin->get_instance_name($instance)
1095 $mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
1096 $mform->set_data($userenroldata);
1097 $validationerrors = $mform->validation($userenroldata, null);
1098 if (empty($validationerrors)) {
1099 require_once($CFG->dirroot . '/enrol/locallib.php');
1100 $manager = new course_enrolment_manager($PAGE, $course);
1101 $result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
1102 } else {
1103 foreach ($validationerrors as $key => $errormessage) {
1104 $errors[] = (object)[
1105 'key' => $key,
1106 'message' => $errormessage
1111 return [
1112 'result' => $result,
1113 'errors' => $errors,
1118 * Returns description of edit_user_enrolment() result value
1120 * @deprecated since 3.8
1121 * @return external_description
1123 public static function edit_user_enrolment_returns() {
1124 return new external_single_structure(
1125 array(
1126 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1127 'errors' => new external_multiple_structure(
1128 new external_single_structure(
1129 array(
1130 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1131 'message' => new external_value(PARAM_TEXT, 'The error message'),
1133 ), 'List of validation errors'
1140 * Mark the edit_user_enrolment web service as deprecated.
1142 * @return bool
1144 public static function edit_user_enrolment_is_deprecated() {
1145 return true;
1149 * Returns description of submit_user_enrolment_form parameters.
1151 * @return external_function_parameters.
1153 public static function submit_user_enrolment_form_parameters() {
1154 return new external_function_parameters([
1155 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
1160 * External function that handles the user enrolment form submission.
1162 * @param string $formdata The user enrolment form data in s URI encoded param string
1163 * @return array An array consisting of the processing result and error flag, if available
1165 public static function submit_user_enrolment_form($formdata) {
1166 global $CFG, $DB, $PAGE;
1168 // Parameter validation.
1169 $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
1171 $data = [];
1172 parse_str($params['formdata'], $data);
1174 $userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST);
1175 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1176 $plugin = enrol_get_plugin($instance->enrol);
1177 $course = get_course($instance->courseid);
1178 $context = context_course::instance($course->id);
1179 self::validate_context($context);
1181 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1182 $customformdata = [
1183 'ue' => $userenrolment,
1184 'modal' => true,
1185 'enrolinstancename' => $plugin->get_instance_name($instance)
1187 $mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data);
1189 if ($validateddata = $mform->get_data()) {
1190 if (!empty($validateddata->duration) && $validateddata->timeend == 0) {
1191 $validateddata->timeend = $validateddata->timestart + $validateddata->duration;
1193 require_once($CFG->dirroot . '/enrol/locallib.php');
1194 $manager = new course_enrolment_manager($PAGE, $course);
1195 $result = $manager->edit_enrolment($userenrolment, $validateddata);
1197 return ['result' => $result];
1198 } else {
1199 return ['result' => false, 'validationerror' => true];
1204 * Returns description of submit_user_enrolment_form() result value
1206 * @return external_description
1208 public static function submit_user_enrolment_form_returns() {
1209 return new external_single_structure([
1210 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1211 'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false),
1216 * Returns description of unenrol_user_enrolment() parameters
1218 * @return external_function_parameters
1220 public static function unenrol_user_enrolment_parameters() {
1221 return new external_function_parameters(
1222 array(
1223 'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
1229 * External function that unenrols a given user enrolment.
1231 * @param int $ueid The user enrolment ID.
1232 * @return array An array consisting of the processing result, errors.
1234 public static function unenrol_user_enrolment($ueid) {
1235 global $CFG, $DB, $PAGE;
1237 $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1238 'ueid' => $ueid
1241 $result = false;
1242 $errors = [];
1244 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
1245 if ($userenrolment) {
1246 $userid = $userenrolment->userid;
1247 $enrolid = $userenrolment->enrolid;
1248 $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
1249 $courseid = $enrol->courseid;
1250 $course = get_course($courseid);
1251 $context = context_course::instance($course->id);
1252 self::validate_context($context);
1253 } else {
1254 $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
1257 // If the userenrolment exists, unenrol the user.
1258 if (!isset($validationerrors)) {
1259 require_once($CFG->dirroot . '/enrol/locallib.php');
1260 $manager = new course_enrolment_manager($PAGE, $course);
1261 $result = $manager->unenrol_user($userenrolment);
1262 } else {
1263 foreach ($validationerrors as $key => $errormessage) {
1264 $errors[] = (object)[
1265 'key' => $key,
1266 'message' => $errormessage
1271 return [
1272 'result' => $result,
1273 'errors' => $errors,
1278 * Returns description of unenrol_user_enrolment() result value
1280 * @return external_description
1282 public static function unenrol_user_enrolment_returns() {
1283 return new external_single_structure(
1284 array(
1285 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1286 'errors' => new external_multiple_structure(
1287 new external_single_structure(
1288 array(
1289 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1290 'message' => new external_value(PARAM_TEXT, 'The error message'),
1292 ), 'List of validation errors'
1300 * Role external functions
1302 * @package core_role
1303 * @category external
1304 * @copyright 2011 Jerome Mouneyrac
1305 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1306 * @since Moodle 2.2
1308 class core_role_external extends external_api {
1311 * Returns description of method parameters
1313 * @return external_function_parameters
1315 public static function assign_roles_parameters() {
1316 return new external_function_parameters(
1317 array(
1318 'assignments' => new external_multiple_structure(
1319 new external_single_structure(
1320 array(
1321 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1322 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1323 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1324 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1325 (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1326 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
1335 * Manual role assignments to users
1337 * @param array $assignments An array of manual role assignment
1339 public static function assign_roles($assignments) {
1340 global $DB;
1342 // Do basic automatic PARAM checks on incoming data, using params description
1343 // If any problems are found then exceptions are thrown with helpful error messages
1344 $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1346 $transaction = $DB->start_delegated_transaction();
1348 foreach ($params['assignments'] as $assignment) {
1349 // Ensure correct context level with a instance id or contextid is passed.
1350 $context = self::get_context_from_params($assignment);
1352 // Ensure the current user is allowed to run this function in the enrolment context.
1353 self::validate_context($context);
1354 require_capability('moodle/role:assign', $context);
1356 // throw an exception if user is not able to assign the role in this context
1357 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1359 if (!array_key_exists($assignment['roleid'], $roles)) {
1360 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1363 role_assign($assignment['roleid'], $assignment['userid'], $context->id);
1366 $transaction->allow_commit();
1370 * Returns description of method result value
1372 * @return null
1374 public static function assign_roles_returns() {
1375 return null;
1380 * Returns description of method parameters
1382 * @return external_function_parameters
1384 public static function unassign_roles_parameters() {
1385 return new external_function_parameters(
1386 array(
1387 'unassignments' => new external_multiple_structure(
1388 new external_single_structure(
1389 array(
1390 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1391 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1392 'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1393 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1394 + (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1395 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
1404 * Unassign roles from users
1406 * @param array $unassignments An array of unassignment
1408 public static function unassign_roles($unassignments) {
1409 global $DB;
1411 // Do basic automatic PARAM checks on incoming data, using params description
1412 // If any problems are found then exceptions are thrown with helpful error messages
1413 $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1415 $transaction = $DB->start_delegated_transaction();
1417 foreach ($params['unassignments'] as $unassignment) {
1418 // Ensure the current user is allowed to run this function in the unassignment context
1419 $context = self::get_context_from_params($unassignment);
1420 self::validate_context($context);
1421 require_capability('moodle/role:assign', $context);
1423 // throw an exception if user is not able to unassign the role in this context
1424 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1425 if (!array_key_exists($unassignment['roleid'], $roles)) {
1426 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1429 role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
1432 $transaction->allow_commit();
1436 * Returns description of method result value
1438 * @return null
1440 public static function unassign_roles_returns() {
1441 return null;