MDL-66269 enrol: A new webservice to search course participants
[moodle.git] / enrol / externallib.php
CommitLineData
e9b66095 1<?php
e9b66095 2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
4615817d 17
e9b66095 18/**
df997f84 19 * External course participation api.
e9b66095 20 *
df997f84
PS
21 * This api is mostly read only, the actual enrol and unenrol
22 * support is in each enrol plugin.
23 *
4615817d
JM
24 * @package core_enrol
25 * @category external
26 * @copyright 2010 Jerome Mouneyrac
e9b66095 27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 */
29
97795859 30defined('MOODLE_INTERNAL') || die();
df997f84 31
e9b66095 32require_once("$CFG->libdir/externallib.php");
33
5d1017e1 34/**
4615817d
JM
35 * Enrol external functions
36 *
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
5d1017e1
JM
42 */
43class core_enrol_external extends external_api {
44
b6b6c7ac
PC
45 /**
46 * Returns description of method parameters
47 *
48 * @return external_function_parameters
49 * @since Moodle 2.4
50 */
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')),
60 )
61 )
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')
68 )
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())
75 )
76 );
77 }
78
79 /**
80 * Return users that have the capabilities for each course specified. For each course and capability specified,
21d3bfb3 81 * a list of the users that are enrolled in the course and have that capability are returned.
b6b6c7ac
PC
82 *
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
86 */
87 public static function get_enrolled_users_with_capability($coursecapabilities, $options) {
88 global $CFG, $DB;
93b47710
MN
89
90 require_once($CFG->dirroot . '/course/lib.php');
b6b6c7ac
PC
91 require_once($CFG->dirroot . "/user/lib.php");
92
93 if (empty($coursecapabilities)) {
94 throw new invalid_parameter_exception('Parameter can not be empty');
95 }
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);
117 }
578a39b8 118 break;
b6b6c7ac
PC
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;
125 }
126 }
127
128 foreach ($params['coursecapabilities'] as $coursecapability) {
129 $courseid = $coursecapability['courseid'];
74df2951 130 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
b6b6c7ac
PC
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.');
135 }
136 if ($courseid == SITEID) {
137 $context = context_system::instance();
138 } else {
139 $context = $coursecontext;
140 }
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));
148 }
149
93b47710
MN
150 course_require_view_participants($context);
151
b6b6c7ac
PC
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);
155 }
156 // The course:enrolereview capability is needed to use this option.
157 if ($onlyactive) {
158 require_capability('moodle/course:enrolreview', $coursecontext);
159 }
160
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;
166
167 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $capability, $groupid, $onlyactive);
1f87a7f6 168 $enrolledparams['courseid'] = $courseid;
b6b6c7ac 169
1f87a7f6
JL
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";
b6b6c7ac
PC
175
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;
181 }
182 }
183 $enrolledusers->close();
184 $courseusers['users'] = $users;
185 $result[] = $courseusers;
186 }
187 }
188 return $result;
189 }
190
191 /**
192 * Returns description of method result value
193 *
194 * @return external_multiple_structure
195 * @since Moodle 2.4
196 */
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(
fb5ce7d3 205 'id' => new external_value(PARAM_INT, 'ID of the user'),
b6b6c7ac
PC
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),
1f87a7f6 224 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
b6b6c7ac
PC
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'),
239 )
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'),
247 )
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')
256 )
257 ), 'user roles', VALUE_OPTIONAL),
258 'preferences' => new external_multiple_structure(
259 new external_single_structure(
260 array(
abdb8f59 261 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
b6b6c7ac
PC
262 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
263 )
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')
271 )
272 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
273 )
274 ), 'List of users that are enrolled in the course and have the specified capability'),
275 )
276 )
277 );
278 }
279
5d1017e1
JM
280 /**
281 * Returns description of method parameters
4615817d 282 *
5d1017e1
JM
283 * @return external_function_parameters
284 */
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'),
7d4c3458
MJ
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),
5d1017e1
JM
294 )
295 );
296 }
297
298 /**
299 * Get list of courses user is enrolled in (only active enrolments are returned).
5d1017e1
JM
300 * Please note the current user must be able to access the course, otherwise the course is not included.
301 *
302 * @param int $userid
7d4c3458 303 * @param bool $returnusercount
5d1017e1
JM
304 * @return array of courses
305 */
7d4c3458 306 public static function get_users_courses($userid, $returnusercount = true) {
93b47710
MN
307 global $CFG, $USER, $DB;
308
309 require_once($CFG->dirroot . '/course/lib.php');
b36ee81e 310 require_once($CFG->libdir . '/completionlib.php');
5d1017e1
JM
311
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
7d4c3458
MJ
314 $params = self::validate_parameters(self::get_users_courses_parameters(),
315 ['userid' => $userid, 'returnusercount' => $returnusercount]);
8e7b8cf0 316 $userid = $params['userid'];
7d4c3458 317 $returnusercount = $params['returnusercount'];
5d1017e1 318
8e7b8cf0 319 $courses = enrol_get_users_courses($userid, true, '*');
5d1017e1
JM
320 $result = array();
321
b36ee81e
JL
322 // Get user data including last access to courses.
323 $user = get_complete_user_data('id', $userid);
8e7b8cf0
JL
324 $sameuser = $USER->id == $userid;
325
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');
331
332 if ($favourites) {
333 $favouritecourseids = array_flip(array_map(
334 function($favourite) {
335 return $favourite->itemid;
336 }, $favourites));
337 }
338 }
b36ee81e 339
5d1017e1 340 foreach ($courses as $course) {
55bcef29 341 $context = context_course::instance($course->id, IGNORE_MISSING);
5d1017e1
JM
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;
347 }
ad7612f5 348
b36ee81e 349 if (!$sameuser and !course_can_view_participants($context)) {
5d1017e1
JM
350 // we need capability to view participants
351 continue;
352 }
353
7d4c3458
MJ
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);
358 }
ad7612f5 359
b36ee81e 360 $displayname = external_format_string(get_course_display_name_for_list($course), $context->id);
170717e2 361 list($course->summary, $course->summaryformat) =
1d3e9546 362 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
d889b587
JL
363 $course->fullname = external_format_string($course->fullname, $context->id);
364 $course->shortname = external_format_string($course->shortname, $context->id);
170717e2 365
80ece084 366 $progress = null;
b36ee81e 367 $completed = null;
8e7b8cf0 368 $completionhascriteria = false;
dfcf8342 369 $completionusertracked = false;
b36ee81e
JL
370
371 // Return only private information if the user should be able to see it.
372 if ($sameuser || completion_can_view_data($userid, $course)) {
373 if ($course->enablecompletion) {
374 $completion = new completion_info($course);
375 $completed = $completion->is_course_complete($userid);
8e7b8cf0 376 $completionhascriteria = $completion->has_criteria();
dfcf8342 377 $completionusertracked = $completion->is_tracked_user($userid);
b36ee81e
JL
378 $progress = \core_completion\progress::get_course_progress_percentage($course, $userid);
379 }
380 }
381
382 $lastaccess = null;
383 // Check if last access is a hidden field.
384 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
385 $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']);
386 if (!$canviewlastaccess) {
387 $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context);
388 }
389
390 if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) {
391 $lastaccess = $user->lastcourseaccess[$course->id];
392 }
393
8e7b8cf0
JL
394 $hidden = false;
395 if ($sameuser) {
396 $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0));
397 }
398
b36ee81e
JL
399 // Retrieve course overview used files.
400 $courselist = new core_course_list_element($course);
401 $overviewfiles = array();
402 foreach ($courselist->get_course_overviewfiles() as $file) {
403 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
404 $file->get_filearea(), null, $file->get_filepath(),
405 $file->get_filename())->out(false);
406 $overviewfiles[] = array(
407 'filename' => $file->get_filename(),
408 'fileurl' => $fileurl,
409 'filesize' => $file->get_filesize(),
410 'filepath' => $file->get_filepath(),
411 'mimetype' => $file->get_mimetype(),
412 'timemodified' => $file->get_timemodified(),
413 );
80ece084
AN
414 }
415
7d4c3458 416 $courseresult = [
27c7ed0b
JL
417 'id' => $course->id,
418 'shortname' => $course->shortname,
419 'fullname' => $course->fullname,
b36ee81e 420 'displayname' => $displayname,
27c7ed0b
JL
421 'idnumber' => $course->idnumber,
422 'visible' => $course->visible,
27c7ed0b
JL
423 'summary' => $course->summary,
424 'summaryformat' => $course->summaryformat,
425 'format' => $course->format,
426 'showgrades' => $course->showgrades,
6db24235 427 'lang' => clean_param($course->lang, PARAM_LANG),
27c7ed0b 428 'enablecompletion' => $course->enablecompletion,
8e7b8cf0 429 'completionhascriteria' => $completionhascriteria,
dfcf8342 430 'completionusertracked' => $completionusertracked,
80ece084
AN
431 'category' => $course->category,
432 'progress' => $progress,
b36ee81e 433 'completed' => $completed,
01c26702
JL
434 'startdate' => $course->startdate,
435 'enddate' => $course->enddate,
b36ee81e
JL
436 'marker' => $course->marker,
437 'lastaccess' => $lastaccess,
8e7b8cf0
JL
438 'isfavourite' => isset($favouritecourseids[$course->id]),
439 'hidden' => $hidden,
b36ee81e 440 'overviewfiles' => $overviewfiles,
7d4c3458
MJ
441 ];
442 if ($returnusercount) {
443 $courseresult['enrolledusercount'] = $enrolledusercount;
444 }
445 $result[] = $courseresult;
5d1017e1 446 }
df997f84 447
5d1017e1
JM
448 return $result;
449 }
450
451 /**
452 * Returns description of method result value
4615817d 453 *
5d1017e1
JM
454 * @return external_description
455 */
456 public static function get_users_courses_returns() {
457 return new external_multiple_structure(
458 new external_single_structure(
459 array(
460 'id' => new external_value(PARAM_INT, 'id of course'),
461 'shortname' => new external_value(PARAM_RAW, 'short name of course'),
462 'fullname' => new external_value(PARAM_RAW, 'long name of course'),
b36ee81e 463 'displayname' => new external_value(PARAM_TEXT, 'course display name for lists.', VALUE_OPTIONAL),
7d4c3458
MJ
464 'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course',
465 VALUE_OPTIONAL),
5d1017e1 466 'idnumber' => new external_value(PARAM_RAW, 'id number of course'),
8e7b8cf0 467 'visible' => new external_value(PARAM_INT, '1 means visible, 0 means not yet visible course'),
170717e2
CC
468 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
469 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
470 'format' => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site', VALUE_OPTIONAL),
471 'showgrades' => new external_value(PARAM_BOOL, 'true if grades are shown, otherwise false', VALUE_OPTIONAL),
472 'lang' => new external_value(PARAM_LANG, 'forced course language', VALUE_OPTIONAL),
ba62015b 473 'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false',
27c7ed0b 474 VALUE_OPTIONAL),
8e7b8cf0 475 'completionhascriteria' => new external_value(PARAM_BOOL, 'If completion criteria is set.', VALUE_OPTIONAL),
dfcf8342 476 'completionusertracked' => new external_value(PARAM_BOOL, 'If the user is completion tracked.', VALUE_OPTIONAL),
27c7ed0b 477 'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
80ece084 478 'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
b36ee81e 479 'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL),
01c26702
JL
480 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
481 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
b36ee81e
JL
482 'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL),
483 'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL),
8e7b8cf0
JL
484 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this course a favourite.', VALUE_OPTIONAL),
485 'hidden' => new external_value(PARAM_BOOL, 'If the user hide the course from the dashboard.', VALUE_OPTIONAL),
b36ee81e 486 'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL),
5d1017e1
JM
487 )
488 )
489 );
490 }
e6acc551 491
a60e8ba5
DW
492 /**
493 * Returns description of method parameters value
494 *
495 * @return external_description
496 */
497 public static function get_potential_users_parameters() {
498 return new external_function_parameters(
499 array(
500 'courseid' => new external_value(PARAM_INT, 'course id'),
501 'enrolid' => new external_value(PARAM_INT, 'enrolment id'),
502 'search' => new external_value(PARAM_RAW, 'query'),
503 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
504 'page' => new external_value(PARAM_INT, 'Page number'),
505 'perpage' => new external_value(PARAM_INT, 'Number per page'),
506 )
507 );
508 }
509
510 /**
511 * Get potential users.
512 *
513 * @param int $courseid Course id
514 * @param int $enrolid Enrolment id
515 * @param string $search The query
516 * @param boolean $searchanywhere Match anywhere in the string
517 * @param int $page Page number
518 * @param int $perpage Max per page
519 * @return array An array of users
520 */
521 public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) {
0fa35b1a
DW
522 global $PAGE, $DB, $CFG;
523
524 require_once($CFG->dirroot.'/enrol/locallib.php');
525 require_once($CFG->dirroot.'/user/lib.php');
a60e8ba5
DW
526
527 $params = self::validate_parameters(
528 self::get_potential_users_parameters(),
529 array(
530 'courseid' => $courseid,
531 'enrolid' => $enrolid,
532 'search' => $search,
533 'searchanywhere' => $searchanywhere,
534 'page' => $page,
535 'perpage' => $perpage
536 )
537 );
538 $context = context_course::instance($params['courseid']);
0fa35b1a
DW
539 try {
540 self::validate_context($context);
541 } catch (Exception $e) {
542 $exceptionparam = new stdClass();
543 $exceptionparam->message = $e->getMessage();
544 $exceptionparam->courseid = $params['courseid'];
545 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
546 }
a60e8ba5
DW
547 require_capability('moodle/course:enrolreview', $context);
548
549 $course = $DB->get_record('course', array('id' => $params['courseid']));
550 $manager = new course_enrolment_manager($PAGE, $course);
551
552 $users = $manager->get_potential_users($params['enrolid'],
553 $params['search'],
554 $params['searchanywhere'],
555 $params['page'],
556 $params['perpage']);
557
558 $results = array();
46ea440b
SA
559 // Add also extra user fields.
560 $requiredfields = array_merge(
561 ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
562 get_extra_user_fields($context)
563 );
0fa35b1a 564 foreach ($users['users'] as $id => $user) {
2cc2001b
DW
565 // Note: We pass the course here to validate that the current user can at least view user details in this course.
566 // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
567 // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
568 // there is no way to find users who aren't in the course in order to enrol them.
af6d2385 569 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
a60e8ba5
DW
570 $results[] = $userdetails;
571 }
572 }
573 return $results;
574 }
575
576 /**
577 * Returns description of method result value
578 *
579 * @return external_description
580 */
581 public static function get_potential_users_returns() {
582 global $CFG;
583 require_once($CFG->dirroot . '/user/externallib.php');
584 return new external_multiple_structure(core_user_external::user_description());
585 }
586
0f308fcd
SR
587 /**
588 * Returns description of method parameters
589 *
590 * @return external_function_parameters
591 */
592 public static function search_users_parameters(): external_function_parameters {
593 return new external_function_parameters(
594 [
595 'courseid' => new external_value(PARAM_INT, 'course id'),
596 'search' => new external_value(PARAM_RAW, 'query'),
597 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
598 'page' => new external_value(PARAM_INT, 'Page number'),
599 'perpage' => new external_value(PARAM_INT, 'Number per page'),
600 ]
601 );
602 }
603
604 /**
605 * Search course participants.
606 *
607 * @param int $courseid Course id
608 * @param string $search The query
609 * @param bool $searchanywhere Match anywhere in the string
610 * @param int $page Page number
611 * @param int $perpage Max per page
612 * @return array An array of users
613 * @throws moodle_exception
614 */
615 public static function search_users(int $courseid, string $search, bool $searchanywhere, int $page, int $perpage): array {
616 global $PAGE, $DB, $CFG;
617
618 require_once($CFG->dirroot.'/enrol/locallib.php');
619 require_once($CFG->dirroot.'/user/lib.php');
620
621 $params = self::validate_parameters(
622 self::search_users_parameters(),
623 [
624 'courseid' => $courseid,
625 'search' => $search,
626 'searchanywhere' => $searchanywhere,
627 'page' => $page,
628 'perpage' => $perpage
629 ]
630 );
631 $context = context_course::instance($params['courseid']);
632 try {
633 self::validate_context($context);
634 } catch (Exception $e) {
635 $exceptionparam = new stdClass();
636 $exceptionparam->message = $e->getMessage();
637 $exceptionparam->courseid = $params['courseid'];
638 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
639 }
640 course_require_view_participants($context);
641
642 $course = $DB->get_record('course', ['id' => $params['courseid']]);
643 $manager = new course_enrolment_manager($PAGE, $course);
644
645 $users = $manager->search_users($params['search'],
646 $params['searchanywhere'],
647 $params['page'],
648 $params['perpage']);
649
650 $results = [];
651 // Add also extra user fields.
652 $requiredfields = array_merge(
653 ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
654 get_extra_user_fields($context)
655 );
656 foreach ($users['users'] as $user) {
657 // Note: We pass the course here to validate that the current user can at least view user details in this course.
658 // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
659 // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
660 // there is no way to find users who aren't in the course in order to enrol them.
661 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
662 $results[] = $userdetails;
663 }
664 }
665 return $results;
666 }
667
668 /**
669 * Returns description of method result value
670 *
671 * @return external_multiple_structure
672 */
673 public static function search_users_returns(): external_multiple_structure {
674 global $CFG;
675 require_once($CFG->dirroot . '/user/externallib.php');
676 return new external_multiple_structure(core_user_external::user_description());
677 }
678
5d1017e1
JM
679 /**
680 * Returns description of method parameters
4615817d 681 *
5d1017e1
JM
682 * @return external_function_parameters
683 */
684 public static function get_enrolled_users_parameters() {
685 return new external_function_parameters(
686 array(
687 'courseid' => new external_value(PARAM_INT, 'course id'),
688 'options' => new external_multiple_structure(
689 new external_single_structure(
690 array(
691 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
692 'value' => new external_value(PARAM_RAW, 'option value')
693 )
3ed74dd1
DC
694 ), 'Option names:
695 * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
b036215a
DP
696 * groupid (integer) return only users in this group id. If the course has groups enabled and this param
697 isn\'t defined, returns all the viewable users.
698 This option requires \'moodle/site:accessallgroups\' on the course context if the
699 user doesn\'t belong to the group.
3ed74dd1
DC
700 * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
701 * userfields (\'string, string, ...\') return only the values of these user fields.
702 * limitfrom (integer) sql limit from.
4bc38ba9
JL
703 * limitnumber (integer) maximum number of returned users.
704 * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
705 * sortdirection (string) ASC or DESC',
706 VALUE_DEFAULT, array()),
5d1017e1
JM
707 )
708 );
709 }
710
711 /**
712 * Get course participants details
4615817d 713 *
5d1017e1
JM
714 * @param int $courseid course id
715 * @param array $options options {
4615817d
JM
716 * 'name' => option name
717 * 'value' => option value
718 * }
5d1017e1
JM
719 * @return array An array of users
720 */
3297d575 721 public static function get_enrolled_users($courseid, $options = array()) {
5d1017e1 722 global $CFG, $USER, $DB;
93b47710
MN
723
724 require_once($CFG->dirroot . '/course/lib.php');
5d1017e1
JM
725 require_once($CFG->dirroot . "/user/lib.php");
726
727 $params = self::validate_parameters(
f4a75854 728 self::get_enrolled_users_parameters(),
5d1017e1
JM
729 array(
730 'courseid'=>$courseid,
731 'options'=>$options
732 )
733 );
734 $withcapability = '';
735 $groupid = 0;
736 $onlyactive = false;
ad7612f5 737 $userfields = array();
3ed74dd1
DC
738 $limitfrom = 0;
739 $limitnumber = 0;
4bc38ba9
JL
740 $sortby = 'us.id';
741 $sortparams = array();
742 $sortdirection = 'ASC';
5d1017e1
JM
743 foreach ($options as $option) {
744 switch ($option['name']) {
745 case 'withcapability':
746 $withcapability = $option['value'];
747 break;
748 case 'groupid':
749 $groupid = (int)$option['value'];
750 break;
751 case 'onlyactive':
752 $onlyactive = !empty($option['value']);
753 break;
ad7612f5
DC
754 case 'userfields':
755 $thefields = explode(',', $option['value']);
756 foreach ($thefields as $f) {
757 $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
758 }
457b82f2 759 break;
3ed74dd1
DC
760 case 'limitfrom' :
761 $limitfrom = clean_param($option['value'], PARAM_INT);
762 break;
763 case 'limitnumber' :
764 $limitnumber = clean_param($option['value'], PARAM_INT);
ad7612f5 765 break;
4bc38ba9
JL
766 case 'sortby':
767 $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
768 if (!in_array($option['value'], $sortallowedvalues)) {
769 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
770 'allowed values are: ' . implode(',', $sortallowedvalues));
771 }
772 if ($option['value'] == 'siteorder') {
773 list($sortby, $sortparams) = users_order_by_sql('us');
774 } else {
775 $sortby = 'us.' . $option['value'];
776 }
777 break;
778 case 'sortdirection':
779 $sortdirection = strtoupper($option['value']);
780 $directionallowedvalues = array('ASC', 'DESC');
781 if (!in_array($sortdirection, $directionallowedvalues)) {
782 throw new invalid_parameter_exception('Invalid value for sortdirection parameter
783 (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
784 }
785 break;
5d1017e1
JM
786 }
787 }
788
74df2951 789 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
55bcef29 790 $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
5d1017e1 791 if ($courseid == SITEID) {
0601e0ee 792 $context = context_system::instance();
5d1017e1
JM
793 } else {
794 $context = $coursecontext;
795 }
796 try {
797 self::validate_context($context);
798 } catch (Exception $e) {
799 $exceptionparam = new stdClass();
800 $exceptionparam->message = $e->getMessage();
801 $exceptionparam->courseid = $params['courseid'];
96d3b93b 802 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
5d1017e1
JM
803 }
804
93b47710
MN
805 course_require_view_participants($context);
806
d5b68e28
PS
807 // to overwrite this parameter, you need role:review capability
808 if ($withcapability) {
809 require_capability('moodle/role:review', $coursecontext);
810 }
811 // need accessallgroups capability if you want to overwrite this option
b036215a 812 if (!empty($groupid) && !groups_is_member($groupid)) {
d5b68e28
PS
813 require_capability('moodle/site:accessallgroups', $coursecontext);
814 }
815 // to overwrite this option, you need course:enrolereview permission
816 if ($onlyactive) {
817 require_capability('moodle/course:enrolreview', $coursecontext);
818 }
819
5d1017e1 820 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
2e4c0c91
FM
821 $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
822 $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
823 $enrolledparams['contextlevel'] = CONTEXT_USER;
b036215a
DP
824
825 $groupjoin = '';
826 if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
827 !has_capability('moodle/site:accessallgroups', $coursecontext)) {
828 // Filter by groups the user can view.
829 $usergroups = groups_get_user_groups($course->id);
830 if (!empty($usergroups['0'])) {
831 list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
832 $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
833 $enrolledparams = array_merge($enrolledparams, $groupparams);
834 } else {
835 // User doesn't belong to any group, so he can't see any user. Return an empty array.
836 return array();
837 }
838 }
1f87a7f6 839 $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
b036215a
DP
840 FROM {user} us
841 JOIN (
842 SELECT DISTINCT u.id $ctxselect
843 FROM {user} u $ctxjoin $groupjoin
844 WHERE u.id IN ($enrolledsql)
845 ) q ON q.id = us.id
1f87a7f6 846 LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
4bc38ba9
JL
847 ORDER BY $sortby $sortdirection";
848 $enrolledparams = array_merge($enrolledparams, $sortparams);
1f87a7f6
JL
849 $enrolledparams['courseid'] = $courseid;
850
3ed74dd1 851 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
5d1017e1
JM
852 $users = array();
853 foreach ($enrolledusers as $user) {
db314f34 854 context_helper::preload_from_record($user);
ad7612f5 855 if ($userdetails = user_get_user_details($user, $course, $userfields)) {
5d1017e1
JM
856 $users[] = $userdetails;
857 }
858 }
859 $enrolledusers->close();
860
861 return $users;
862 }
e6acc551 863
5d1017e1
JM
864 /**
865 * Returns description of method result value
4615817d 866 *
5d1017e1
JM
867 * @return external_description
868 */
869 public static function get_enrolled_users_returns() {
870 return new external_multiple_structure(
871 new external_single_structure(
872 array(
61cca0b7 873 'id' => new external_value(PARAM_INT, 'ID of the user'),
5d1017e1
JM
874 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
875 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
876 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
877 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
878 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
071e68f9 879 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
5d1017e1
JM
880 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
881 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
882 'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
883 'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
884 'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
885 'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
886 'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
887 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
888 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
3a3f3b22 889 'idnumber' => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
5d1017e1
JM
890 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
891 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
892 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
1f87a7f6 893 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
5d1017e1 894 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
93ce0e82 895 'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
5d1017e1
JM
896 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
897 'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
898 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
ad7612f5
DC
899 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
900 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
5d1017e1
JM
901 'customfields' => new external_multiple_structure(
902 new external_single_structure(
903 array(
904 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
905 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
906 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
907 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
908 )
909 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
910 'groups' => new external_multiple_structure(
911 new external_single_structure(
912 array(
913 'id' => new external_value(PARAM_INT, 'group id'),
914 'name' => new external_value(PARAM_RAW, 'group name'),
915 'description' => new external_value(PARAM_RAW, 'group description'),
93ce0e82 916 'descriptionformat' => new external_format_value('description'),
5d1017e1
JM
917 )
918 ), 'user groups', VALUE_OPTIONAL),
919 'roles' => new external_multiple_structure(
920 new external_single_structure(
921 array(
922 'roleid' => new external_value(PARAM_INT, 'role id'),
923 'name' => new external_value(PARAM_RAW, 'role name'),
924 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
925 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
926 )
927 ), 'user roles', VALUE_OPTIONAL),
928 'preferences' => new external_multiple_structure(
929 new external_single_structure(
930 array(
abdb8f59 931 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
5d1017e1
JM
932 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
933 )
934 ), 'User preferences', VALUE_OPTIONAL),
935 'enrolledcourses' => new external_multiple_structure(
936 new external_single_structure(
937 array(
938 'id' => new external_value(PARAM_INT, 'Id of the course'),
939 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
940 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
941 )
942 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
943 )
944 )
945 );
946 }
947
be9bf94e
RT
948 /**
949 * Returns description of get_course_enrolment_methods() parameters
950 *
951 * @return external_function_parameters
952 */
953 public static function get_course_enrolment_methods_parameters() {
954 return new external_function_parameters(
955 array(
956 'courseid' => new external_value(PARAM_INT, 'Course id')
957 )
958 );
959 }
960
961 /**
962 * Get list of active course enrolment methods for current user.
963 *
964 * @param int $courseid
965 * @return array of course enrolment methods
4323a973 966 * @throws moodle_exception
be9bf94e
RT
967 */
968 public static function get_course_enrolment_methods($courseid) {
4323a973 969 global $DB;
be9bf94e
RT
970
971 $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
67ee1030 972 self::validate_context(context_system::instance());
4323a973
JL
973
974 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
beff3806 975 if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
4323a973
JL
976 throw new moodle_exception('coursehidden');
977 }
be9bf94e
RT
978
979 $result = array();
980 $enrolinstances = enrol_get_instances($params['courseid'], true);
981 foreach ($enrolinstances as $enrolinstance) {
982 if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
983 if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
984 $result[] = (array) $instanceinfo;
985 }
986 }
987 }
988 return $result;
989 }
990
991 /**
992 * Returns description of get_course_enrolment_methods() result value
993 *
994 * @return external_description
995 */
996 public static function get_course_enrolment_methods_returns() {
997 return new external_multiple_structure(
998 new external_single_structure(
999 array(
1000 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
1001 'courseid' => new external_value(PARAM_INT, 'id of course'),
1002 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
1003 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
1004 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
1005 'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
1006 )
1007 )
1008 );
1009 }
58c20c81
JP
1010
1011 /**
1012 * Returns description of edit_user_enrolment() parameters
1013 *
2d4ce64d 1014 * @deprecated since 3.8
58c20c81
JP
1015 * @return external_function_parameters
1016 */
1017 public static function edit_user_enrolment_parameters() {
1018 return new external_function_parameters(
1019 array(
1020 'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
1021 'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
1022 'status' => new external_value(PARAM_INT, 'Enrolment status'),
1023 'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
1024 'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
1025 )
1026 );
1027 }
1028
1029 /**
1030 * External function that updates a given user enrolment.
1031 *
2d4ce64d 1032 * @deprecated since 3.8
58c20c81
JP
1033 * @param int $courseid The course ID.
1034 * @param int $ueid The user enrolment ID.
1035 * @param int $status The enrolment status.
1036 * @param int $timestart Enrolment start timestamp.
1037 * @param int $timeend Enrolment end timestamp.
1038 * @return array An array consisting of the processing result, errors and form output, if available.
1039 */
1040 public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
1041 global $CFG, $DB, $PAGE;
1042
1043 $params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
1044 'courseid' => $courseid,
1045 'ueid' => $ueid,
1046 'status' => $status,
1047 'timestart' => $timestart,
1048 'timeend' => $timeend,
1049 ]);
1050
1051 $course = get_course($courseid);
1052 $context = context_course::instance($course->id);
1053 self::validate_context($context);
1054
1055 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
1056 $userenroldata = [
1057 'status' => $params['status'],
1058 'timestart' => $params['timestart'],
1059 'timeend' => $params['timeend'],
1060 ];
1061
1062 $result = false;
1063 $errors = [];
1064
1065 // Validate data against the edit user enrolment form.
af835c24
MN
1066 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1067 $plugin = enrol_get_plugin($instance->enrol);
58c20c81
JP
1068 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1069 $customformdata = [
1070 'ue' => $userenrolment,
1071 'modal' => true,
af835c24 1072 'enrolinstancename' => $plugin->get_instance_name($instance)
58c20c81
JP
1073 ];
1074 $mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
1075 $mform->set_data($userenroldata);
1076 $validationerrors = $mform->validation($userenroldata, null);
1077 if (empty($validationerrors)) {
1078 require_once($CFG->dirroot . '/enrol/locallib.php');
1079 $manager = new course_enrolment_manager($PAGE, $course);
1080 $result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
1081 } else {
1082 foreach ($validationerrors as $key => $errormessage) {
1083 $errors[] = (object)[
1084 'key' => $key,
1085 'message' => $errormessage
1086 ];
1087 }
1088 }
1089
1090 return [
1091 'result' => $result,
1092 'errors' => $errors,
1093 ];
1094 }
1095
1096 /**
1097 * Returns description of edit_user_enrolment() result value
1098 *
2d4ce64d 1099 * @deprecated since 3.8
58c20c81
JP
1100 * @return external_description
1101 */
1102 public static function edit_user_enrolment_returns() {
c8351261
MG
1103 return new external_single_structure(
1104 array(
1105 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1106 'errors' => new external_multiple_structure(
1107 new external_single_structure(
1108 array(
1109 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1110 'message' => new external_value(PARAM_TEXT, 'The error message'),
1111 )
1112 ), 'List of validation errors'
1113 ),
1114 )
1115 );
1116 }
1117
2d4ce64d
SR
1118 /**
1119 * Mark the edit_user_enrolment web service as deprecated.
1120 *
1121 * @return bool
1122 */
1123 public static function edit_user_enrolment_is_deprecated() {
1124 return true;
1125 }
1126
1127 /**
1128 * Returns description of submit_user_enrolment_form parameters.
1129 *
1130 * @return external_function_parameters.
1131 */
1132 public static function submit_user_enrolment_form_parameters() {
1133 return new external_function_parameters([
1134 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
1135 ]);
1136 }
1137
1138 /**
1139 * External function that handles the user enrolment form submission.
1140 *
1141 * @param string $formdata The user enrolment form data in s URI encoded param string
1142 * @return array An array consisting of the processing result and error flag, if available
1143 */
1144 public static function submit_user_enrolment_form($formdata) {
1145 global $CFG, $DB, $PAGE;
1146
1147 // Parameter validation.
1148 $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
1149
1150 $data = [];
1151 parse_str($params['formdata'], $data);
1152
1153 $userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST);
1154 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1155 $plugin = enrol_get_plugin($instance->enrol);
1156 $course = get_course($instance->courseid);
1157 $context = context_course::instance($course->id);
1158 self::validate_context($context);
1159
1160 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1161 $customformdata = [
1162 'ue' => $userenrolment,
1163 'modal' => true,
1164 'enrolinstancename' => $plugin->get_instance_name($instance)
1165 ];
1166 $mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data);
1167
1168 if ($validateddata = $mform->get_data()) {
1169 if (!empty($validateddata->duration) && $validateddata->timeend == 0) {
1170 $validateddata->timeend = $validateddata->timestart + $validateddata->duration;
1171 }
1172 require_once($CFG->dirroot . '/enrol/locallib.php');
1173 $manager = new course_enrolment_manager($PAGE, $course);
1174 $result = $manager->edit_enrolment($userenrolment, $validateddata);
1175
1176 return ['result' => $result];
1177 } else {
1178 return ['result' => false, 'validationerror' => true];
1179 }
1180 }
1181
1182 /**
1183 * Returns description of submit_user_enrolment_form() result value
1184 *
1185 * @return external_description
1186 */
1187 public static function submit_user_enrolment_form_returns() {
1188 return new external_single_structure([
1189 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1190 'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false),
1191 ]);
1192 }
1193
c8351261
MG
1194 /**
1195 * Returns description of unenrol_user_enrolment() parameters
1196 *
1197 * @return external_function_parameters
1198 */
1199 public static function unenrol_user_enrolment_parameters() {
1200 return new external_function_parameters(
1201 array(
1202 'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
1203 )
1204 );
1205 }
1206
1207 /**
1208 * External function that unenrols a given user enrolment.
1209 *
1210 * @param int $ueid The user enrolment ID.
1211 * @return array An array consisting of the processing result, errors.
1212 */
1213 public static function unenrol_user_enrolment($ueid) {
1214 global $CFG, $DB, $PAGE;
1215
1216 $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1217 'ueid' => $ueid
1218 ]);
1219
1220 $result = false;
1221 $errors = [];
1222
1223 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
1224 if ($userenrolment) {
1225 $userid = $userenrolment->userid;
1226 $enrolid = $userenrolment->enrolid;
1227 $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
1228 $courseid = $enrol->courseid;
1229 $course = get_course($courseid);
1230 $context = context_course::instance($course->id);
1231 self::validate_context($context);
1232 } else {
1233 $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
1234 }
1235
1236 // If the userenrolment exists, unenrol the user.
1237 if (!isset($validationerrors)) {
1238 require_once($CFG->dirroot . '/enrol/locallib.php');
1239 $manager = new course_enrolment_manager($PAGE, $course);
1240 $result = $manager->unenrol_user($userenrolment);
1241 } else {
1242 foreach ($validationerrors as $key => $errormessage) {
1243 $errors[] = (object)[
1244 'key' => $key,
1245 'message' => $errormessage
1246 ];
1247 }
1248 }
1249
1250 return [
1251 'result' => $result,
1252 'errors' => $errors,
1253 ];
1254 }
1255
1256 /**
1257 * Returns description of unenrol_user_enrolment() result value
1258 *
1259 * @return external_description
1260 */
1261 public static function unenrol_user_enrolment_returns() {
58c20c81
JP
1262 return new external_single_structure(
1263 array(
1264 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1265 'errors' => new external_multiple_structure(
1266 new external_single_structure(
1267 array(
1268 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1269 'message' => new external_value(PARAM_TEXT, 'The error message'),
1270 )
1271 ), 'List of validation errors'
1272 ),
1273 )
1274 );
1275 }
5d1017e1
JM
1276}
1277
1278/**
4615817d
JM
1279 * Role external functions
1280 *
1281 * @package core_role
1282 * @category external
1283 * @copyright 2011 Jerome Mouneyrac
1284 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1285 * @since Moodle 2.2
5d1017e1
JM
1286 */
1287class core_role_external extends external_api {
1288
1289 /**
1290 * Returns description of method parameters
4615817d 1291 *
5d1017e1
JM
1292 * @return external_function_parameters
1293 */
1294 public static function assign_roles_parameters() {
1295 return new external_function_parameters(
1296 array(
1297 'assignments' => new external_multiple_structure(
1298 new external_single_structure(
1299 array(
1300 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1301 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
3a68f798
AA
1302 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1303 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1304 (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1305 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
5d1017e1
JM
1306 )
1307 )
1308 )
1309 )
1310 );
1311 }
1312
1313 /**
1314 * Manual role assignments to users
1315 *
4615817d 1316 * @param array $assignments An array of manual role assignment
5d1017e1
JM
1317 */
1318 public static function assign_roles($assignments) {
1319 global $DB;
1320
1321 // Do basic automatic PARAM checks on incoming data, using params description
1322 // If any problems are found then exceptions are thrown with helpful error messages
1323 $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1324
1325 $transaction = $DB->start_delegated_transaction();
1326
1327 foreach ($params['assignments'] as $assignment) {
3a68f798 1328 // Ensure correct context level with a instance id or contextid is passed.
5b23d9ad 1329 $context = self::get_context_from_params($assignment);
3a68f798
AA
1330
1331 // Ensure the current user is allowed to run this function in the enrolment context.
5d1017e1
JM
1332 self::validate_context($context);
1333 require_capability('moodle/role:assign', $context);
1334
1335 // throw an exception if user is not able to assign the role in this context
1336 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1337
12fc8acf 1338 if (!array_key_exists($assignment['roleid'], $roles)) {
5d1017e1
JM
1339 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1340 }
1341
3a68f798 1342 role_assign($assignment['roleid'], $assignment['userid'], $context->id);
5d1017e1
JM
1343 }
1344
1345 $transaction->allow_commit();
1346 }
1347
1348 /**
1349 * Returns description of method result value
4615817d
JM
1350 *
1351 * @return null
5d1017e1
JM
1352 */
1353 public static function assign_roles_returns() {
1354 return null;
1355 }
1356
1357
1358 /**
1359 * Returns description of method parameters
4615817d 1360 *
5d1017e1
JM
1361 * @return external_function_parameters
1362 */
1363 public static function unassign_roles_parameters() {
1364 return new external_function_parameters(
1365 array(
1366 'unassignments' => new external_multiple_structure(
1367 new external_single_structure(
1368 array(
1369 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1370 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1e631792
AA
1371 'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1372 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1373+ (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1374 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
5d1017e1
JM
1375 )
1376 )
1377 )
1378 )
1379 );
1380 }
1381
1382 /**
1383 * Unassign roles from users
1384 *
4615817d 1385 * @param array $unassignments An array of unassignment
5d1017e1
JM
1386 */
1387 public static function unassign_roles($unassignments) {
1388 global $DB;
1389
1390 // Do basic automatic PARAM checks on incoming data, using params description
1391 // If any problems are found then exceptions are thrown with helpful error messages
1392 $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1393
1394 $transaction = $DB->start_delegated_transaction();
1395
1396 foreach ($params['unassignments'] as $unassignment) {
1397 // Ensure the current user is allowed to run this function in the unassignment context
1e631792 1398 $context = self::get_context_from_params($unassignment);
5d1017e1
JM
1399 self::validate_context($context);
1400 require_capability('moodle/role:assign', $context);
1401
1402 // throw an exception if user is not able to unassign the role in this context
1403 $roles = get_assignable_roles($context, ROLENAME_SHORT);
12fc8acf 1404 if (!array_key_exists($unassignment['roleid'], $roles)) {
5d1017e1
JM
1405 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1406 }
1407
1e631792 1408 role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
5d1017e1
JM
1409 }
1410
1411 $transaction->allow_commit();
1412 }
1413
1414 /**
1415 * Returns description of method result value
4615817d 1416 *
5d1017e1
JM
1417 * @return null
1418 */
1419 public static function unassign_roles_returns() {
1420 return null;
1421 }
1422}