MDL-69869 enrol: Fixing intendation for travis.
[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'),
8c84eeee 463 'displayname' => new external_value(PARAM_RAW, '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
1de4baf1 642 $course = get_course($params['courseid']);
0f308fcd
SR
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) {
0f308fcd
SR
657 if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
658 $results[] = $userdetails;
659 }
660 }
661 return $results;
662 }
663
664 /**
665 * Returns description of method result value
666 *
667 * @return external_multiple_structure
668 */
669 public static function search_users_returns(): external_multiple_structure {
670 global $CFG;
671 require_once($CFG->dirroot . '/user/externallib.php');
672 return new external_multiple_structure(core_user_external::user_description());
673 }
674
5d1017e1
JM
675 /**
676 * Returns description of method parameters
4615817d 677 *
5d1017e1
JM
678 * @return external_function_parameters
679 */
680 public static function get_enrolled_users_parameters() {
681 return new external_function_parameters(
65a1d6e8 682 [
5d1017e1
JM
683 'courseid' => new external_value(PARAM_INT, 'course id'),
684 'options' => new external_multiple_structure(
685 new external_single_structure(
65a1d6e8 686 [
5d1017e1
JM
687 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
688 'value' => new external_value(PARAM_RAW, 'option value')
65a1d6e8 689 ]
3ed74dd1
DC
690 ), 'Option names:
691 * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
b036215a
DP
692 * groupid (integer) return only users in this group id. If the course has groups enabled and this param
693 isn\'t defined, returns all the viewable users.
694 This option requires \'moodle/site:accessallgroups\' on the course context if the
695 user doesn\'t belong to the group.
e53bc6a1
IT
696 * onlyactive (integer) return only users with active enrolments and matching time restrictions.
697 This option requires \'moodle/course:enrolreview\' on the course context.
698 Please note that this option can\'t
699 be used together with onlysuspended (only one can be active).
700 * onlysuspended (integer) return only suspended users. This option requires
701 \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
702 be used together with onlyactive (only one can be active).
3ed74dd1
DC
703 * userfields (\'string, string, ...\') return only the values of these user fields.
704 * limitfrom (integer) sql limit from.
4bc38ba9
JL
705 * limitnumber (integer) maximum number of returned users.
706 * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
707 * sortdirection (string) ASC or DESC',
65a1d6e8
IT
708 VALUE_DEFAULT, []),
709 ]
5d1017e1
JM
710 );
711 }
712
713 /**
714 * Get course participants details
4615817d 715 *
5d1017e1
JM
716 * @param int $courseid course id
717 * @param array $options options {
4615817d
JM
718 * 'name' => option name
719 * 'value' => option value
720 * }
5d1017e1
JM
721 * @return array An array of users
722 */
65a1d6e8 723 public static function get_enrolled_users($courseid, $options = []) {
5d1017e1 724 global $CFG, $USER, $DB;
93b47710
MN
725
726 require_once($CFG->dirroot . '/course/lib.php');
5d1017e1
JM
727 require_once($CFG->dirroot . "/user/lib.php");
728
729 $params = self::validate_parameters(
f4a75854 730 self::get_enrolled_users_parameters(),
65a1d6e8 731 [
5d1017e1
JM
732 'courseid'=>$courseid,
733 'options'=>$options
65a1d6e8 734 ]
5d1017e1
JM
735 );
736 $withcapability = '';
737 $groupid = 0;
738 $onlyactive = false;
e53bc6a1 739 $onlysuspended = false;
65a1d6e8 740 $userfields = [];
3ed74dd1
DC
741 $limitfrom = 0;
742 $limitnumber = 0;
4bc38ba9 743 $sortby = 'us.id';
65a1d6e8 744 $sortparams = [];
4bc38ba9 745 $sortdirection = 'ASC';
5d1017e1
JM
746 foreach ($options as $option) {
747 switch ($option['name']) {
aa1691d8
IT
748 case 'withcapability':
749 $withcapability = $option['value'];
750 break;
751 case 'groupid':
752 $groupid = (int)$option['value'];
753 break;
754 case 'onlyactive':
755 $onlyactive = !empty($option['value']);
756 break;
757 case 'onlysuspended':
758 $onlysuspended = !empty($option['value']);
759 break;
760 case 'userfields':
761 $thefields = explode(',', $option['value']);
762 foreach ($thefields as $f) {
763 $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
764 }
765 break;
766 case 'limitfrom' :
767 $limitfrom = clean_param($option['value'], PARAM_INT);
768 break;
769 case 'limitnumber' :
770 $limitnumber = clean_param($option['value'], PARAM_INT);
771 break;
772 case 'sortby':
773 $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
774 if (!in_array($option['value'], $sortallowedvalues)) {
775 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
776 $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
777 }
778 if ($option['value'] == 'siteorder') {
779 list($sortby, $sortparams) = users_order_by_sql('us');
780 } else {
781 $sortby = 'us.' . $option['value'];
782 }
783 break;
784 case 'sortdirection':
785 $sortdirection = strtoupper($option['value']);
786 $directionallowedvalues = ['ASC', 'DESC'];
787 if (!in_array($sortdirection, $directionallowedvalues)) {
788 throw new invalid_parameter_exception('Invalid value for sortdirection parameter
4bc38ba9 789 (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
aa1691d8
IT
790 }
791 break;
5d1017e1
JM
792 }
793 }
794
65a1d6e8 795 $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
55bcef29 796 $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
5d1017e1 797 if ($courseid == SITEID) {
0601e0ee 798 $context = context_system::instance();
5d1017e1
JM
799 } else {
800 $context = $coursecontext;
801 }
802 try {
803 self::validate_context($context);
804 } catch (Exception $e) {
805 $exceptionparam = new stdClass();
806 $exceptionparam->message = $e->getMessage();
807 $exceptionparam->courseid = $params['courseid'];
96d3b93b 808 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
5d1017e1
JM
809 }
810
93b47710
MN
811 course_require_view_participants($context);
812
d5b68e28
PS
813 // to overwrite this parameter, you need role:review capability
814 if ($withcapability) {
815 require_capability('moodle/role:review', $coursecontext);
816 }
817 // need accessallgroups capability if you want to overwrite this option
b036215a 818 if (!empty($groupid) && !groups_is_member($groupid)) {
d5b68e28
PS
819 require_capability('moodle/site:accessallgroups', $coursecontext);
820 }
821 // to overwrite this option, you need course:enrolereview permission
e53bc6a1 822 if ($onlyactive || $onlysuspended) {
d5b68e28
PS
823 require_capability('moodle/course:enrolreview', $coursecontext);
824 }
825
e53bc6a1
IT
826 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
827 $onlysuspended);
2e4c0c91
FM
828 $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
829 $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
830 $enrolledparams['contextlevel'] = CONTEXT_USER;
b036215a
DP
831
832 $groupjoin = '';
833 if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
834 !has_capability('moodle/site:accessallgroups', $coursecontext)) {
835 // Filter by groups the user can view.
836 $usergroups = groups_get_user_groups($course->id);
837 if (!empty($usergroups['0'])) {
838 list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
839 $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
840 $enrolledparams = array_merge($enrolledparams, $groupparams);
841 } else {
842 // User doesn't belong to any group, so he can't see any user. Return an empty array.
65a1d6e8 843 return [];
b036215a
DP
844 }
845 }
1f87a7f6 846 $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
b036215a
DP
847 FROM {user} us
848 JOIN (
849 SELECT DISTINCT u.id $ctxselect
850 FROM {user} u $ctxjoin $groupjoin
851 WHERE u.id IN ($enrolledsql)
852 ) q ON q.id = us.id
1f87a7f6 853 LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
4bc38ba9
JL
854 ORDER BY $sortby $sortdirection";
855 $enrolledparams = array_merge($enrolledparams, $sortparams);
1f87a7f6
JL
856 $enrolledparams['courseid'] = $courseid;
857
3ed74dd1 858 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
65a1d6e8 859 $users = [];
5d1017e1 860 foreach ($enrolledusers as $user) {
db314f34 861 context_helper::preload_from_record($user);
ad7612f5 862 if ($userdetails = user_get_user_details($user, $course, $userfields)) {
5d1017e1
JM
863 $users[] = $userdetails;
864 }
865 }
866 $enrolledusers->close();
867
868 return $users;
869 }
e6acc551 870
5d1017e1
JM
871 /**
872 * Returns description of method result value
4615817d 873 *
5d1017e1
JM
874 * @return external_description
875 */
876 public static function get_enrolled_users_returns() {
877 return new external_multiple_structure(
878 new external_single_structure(
65a1d6e8 879 [
61cca0b7 880 'id' => new external_value(PARAM_INT, 'ID of the user'),
5d1017e1
JM
881 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
882 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
883 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
884 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
885 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
071e68f9 886 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
5d1017e1
JM
887 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
888 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
889 'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
890 'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
891 'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
892 'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
893 'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
894 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
895 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
3a3f3b22 896 'idnumber' => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
5d1017e1
JM
897 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
898 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
899 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
1f87a7f6 900 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
5d1017e1 901 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
93ce0e82 902 'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
5d1017e1
JM
903 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
904 'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
905 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
ad7612f5
DC
906 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
907 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
5d1017e1
JM
908 'customfields' => new external_multiple_structure(
909 new external_single_structure(
65a1d6e8 910 [
5d1017e1
JM
911 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
912 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
913 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
914 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
65a1d6e8 915 ]
5d1017e1
JM
916 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
917 'groups' => new external_multiple_structure(
918 new external_single_structure(
65a1d6e8 919 [
5d1017e1
JM
920 'id' => new external_value(PARAM_INT, 'group id'),
921 'name' => new external_value(PARAM_RAW, 'group name'),
922 'description' => new external_value(PARAM_RAW, 'group description'),
93ce0e82 923 'descriptionformat' => new external_format_value('description'),
65a1d6e8 924 ]
5d1017e1
JM
925 ), 'user groups', VALUE_OPTIONAL),
926 'roles' => new external_multiple_structure(
927 new external_single_structure(
65a1d6e8 928 [
5d1017e1
JM
929 'roleid' => new external_value(PARAM_INT, 'role id'),
930 'name' => new external_value(PARAM_RAW, 'role name'),
931 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
932 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
65a1d6e8 933 ]
5d1017e1
JM
934 ), 'user roles', VALUE_OPTIONAL),
935 'preferences' => new external_multiple_structure(
936 new external_single_structure(
65a1d6e8 937 [
abdb8f59 938 'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
5d1017e1 939 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
65a1d6e8 940 ]
5d1017e1
JM
941 ), 'User preferences', VALUE_OPTIONAL),
942 'enrolledcourses' => new external_multiple_structure(
943 new external_single_structure(
65a1d6e8 944 [
5d1017e1
JM
945 'id' => new external_value(PARAM_INT, 'Id of the course'),
946 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
947 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
65a1d6e8 948 ]
5d1017e1 949 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
65a1d6e8 950 ]
5d1017e1
JM
951 )
952 );
953 }
954
be9bf94e
RT
955 /**
956 * Returns description of get_course_enrolment_methods() parameters
957 *
958 * @return external_function_parameters
959 */
960 public static function get_course_enrolment_methods_parameters() {
961 return new external_function_parameters(
962 array(
963 'courseid' => new external_value(PARAM_INT, 'Course id')
964 )
965 );
966 }
967
968 /**
969 * Get list of active course enrolment methods for current user.
970 *
971 * @param int $courseid
972 * @return array of course enrolment methods
4323a973 973 * @throws moodle_exception
be9bf94e
RT
974 */
975 public static function get_course_enrolment_methods($courseid) {
4323a973 976 global $DB;
be9bf94e
RT
977
978 $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
67ee1030 979 self::validate_context(context_system::instance());
4323a973
JL
980
981 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
beff3806 982 if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
4323a973
JL
983 throw new moodle_exception('coursehidden');
984 }
be9bf94e
RT
985
986 $result = array();
987 $enrolinstances = enrol_get_instances($params['courseid'], true);
988 foreach ($enrolinstances as $enrolinstance) {
989 if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
990 if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
991 $result[] = (array) $instanceinfo;
992 }
993 }
994 }
995 return $result;
996 }
997
998 /**
999 * Returns description of get_course_enrolment_methods() result value
1000 *
1001 * @return external_description
1002 */
1003 public static function get_course_enrolment_methods_returns() {
1004 return new external_multiple_structure(
1005 new external_single_structure(
1006 array(
1007 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
1008 'courseid' => new external_value(PARAM_INT, 'id of course'),
1009 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
1010 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
1011 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
1012 'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
1013 )
1014 )
1015 );
1016 }
58c20c81
JP
1017
1018 /**
1019 * Returns description of edit_user_enrolment() parameters
1020 *
2d4ce64d 1021 * @deprecated since 3.8
58c20c81
JP
1022 * @return external_function_parameters
1023 */
1024 public static function edit_user_enrolment_parameters() {
1025 return new external_function_parameters(
1026 array(
1027 'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
1028 'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
1029 'status' => new external_value(PARAM_INT, 'Enrolment status'),
1030 'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
1031 'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
1032 )
1033 );
1034 }
1035
1036 /**
1037 * External function that updates a given user enrolment.
1038 *
2d4ce64d 1039 * @deprecated since 3.8
58c20c81
JP
1040 * @param int $courseid The course ID.
1041 * @param int $ueid The user enrolment ID.
1042 * @param int $status The enrolment status.
1043 * @param int $timestart Enrolment start timestamp.
1044 * @param int $timeend Enrolment end timestamp.
1045 * @return array An array consisting of the processing result, errors and form output, if available.
1046 */
1047 public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
1048 global $CFG, $DB, $PAGE;
1049
1050 $params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
1051 'courseid' => $courseid,
1052 'ueid' => $ueid,
1053 'status' => $status,
1054 'timestart' => $timestart,
1055 'timeend' => $timeend,
1056 ]);
1057
1058 $course = get_course($courseid);
1059 $context = context_course::instance($course->id);
1060 self::validate_context($context);
1061
1062 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
1063 $userenroldata = [
1064 'status' => $params['status'],
1065 'timestart' => $params['timestart'],
1066 'timeend' => $params['timeend'],
1067 ];
1068
1069 $result = false;
1070 $errors = [];
1071
1072 // Validate data against the edit user enrolment form.
af835c24
MN
1073 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1074 $plugin = enrol_get_plugin($instance->enrol);
58c20c81
JP
1075 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1076 $customformdata = [
1077 'ue' => $userenrolment,
1078 'modal' => true,
af835c24 1079 'enrolinstancename' => $plugin->get_instance_name($instance)
58c20c81
JP
1080 ];
1081 $mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
1082 $mform->set_data($userenroldata);
1083 $validationerrors = $mform->validation($userenroldata, null);
1084 if (empty($validationerrors)) {
1085 require_once($CFG->dirroot . '/enrol/locallib.php');
1086 $manager = new course_enrolment_manager($PAGE, $course);
1087 $result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
1088 } else {
1089 foreach ($validationerrors as $key => $errormessage) {
1090 $errors[] = (object)[
1091 'key' => $key,
1092 'message' => $errormessage
1093 ];
1094 }
1095 }
1096
1097 return [
1098 'result' => $result,
1099 'errors' => $errors,
1100 ];
1101 }
1102
1103 /**
1104 * Returns description of edit_user_enrolment() result value
1105 *
2d4ce64d 1106 * @deprecated since 3.8
58c20c81
JP
1107 * @return external_description
1108 */
1109 public static function edit_user_enrolment_returns() {
c8351261
MG
1110 return new external_single_structure(
1111 array(
1112 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1113 'errors' => new external_multiple_structure(
1114 new external_single_structure(
1115 array(
1116 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1117 'message' => new external_value(PARAM_TEXT, 'The error message'),
1118 )
1119 ), 'List of validation errors'
1120 ),
1121 )
1122 );
1123 }
1124
2d4ce64d
SR
1125 /**
1126 * Mark the edit_user_enrolment web service as deprecated.
1127 *
1128 * @return bool
1129 */
1130 public static function edit_user_enrolment_is_deprecated() {
1131 return true;
1132 }
1133
1134 /**
1135 * Returns description of submit_user_enrolment_form parameters.
1136 *
1137 * @return external_function_parameters.
1138 */
1139 public static function submit_user_enrolment_form_parameters() {
1140 return new external_function_parameters([
1141 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
1142 ]);
1143 }
1144
1145 /**
1146 * External function that handles the user enrolment form submission.
1147 *
1148 * @param string $formdata The user enrolment form data in s URI encoded param string
1149 * @return array An array consisting of the processing result and error flag, if available
1150 */
1151 public static function submit_user_enrolment_form($formdata) {
1152 global $CFG, $DB, $PAGE;
1153
1154 // Parameter validation.
1155 $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
1156
1157 $data = [];
1158 parse_str($params['formdata'], $data);
1159
1160 $userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST);
1161 $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1162 $plugin = enrol_get_plugin($instance->enrol);
1163 $course = get_course($instance->courseid);
1164 $context = context_course::instance($course->id);
1165 self::validate_context($context);
1166
1167 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1168 $customformdata = [
1169 'ue' => $userenrolment,
1170 'modal' => true,
1171 'enrolinstancename' => $plugin->get_instance_name($instance)
1172 ];
1173 $mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data);
1174
1175 if ($validateddata = $mform->get_data()) {
1176 if (!empty($validateddata->duration) && $validateddata->timeend == 0) {
1177 $validateddata->timeend = $validateddata->timestart + $validateddata->duration;
1178 }
1179 require_once($CFG->dirroot . '/enrol/locallib.php');
1180 $manager = new course_enrolment_manager($PAGE, $course);
1181 $result = $manager->edit_enrolment($userenrolment, $validateddata);
1182
1183 return ['result' => $result];
1184 } else {
1185 return ['result' => false, 'validationerror' => true];
1186 }
1187 }
1188
1189 /**
1190 * Returns description of submit_user_enrolment_form() result value
1191 *
1192 * @return external_description
1193 */
1194 public static function submit_user_enrolment_form_returns() {
1195 return new external_single_structure([
1196 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1197 'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false),
1198 ]);
1199 }
1200
c8351261
MG
1201 /**
1202 * Returns description of unenrol_user_enrolment() parameters
1203 *
1204 * @return external_function_parameters
1205 */
1206 public static function unenrol_user_enrolment_parameters() {
1207 return new external_function_parameters(
1208 array(
1209 'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
1210 )
1211 );
1212 }
1213
1214 /**
1215 * External function that unenrols a given user enrolment.
1216 *
1217 * @param int $ueid The user enrolment ID.
1218 * @return array An array consisting of the processing result, errors.
1219 */
1220 public static function unenrol_user_enrolment($ueid) {
1221 global $CFG, $DB, $PAGE;
1222
1223 $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1224 'ueid' => $ueid
1225 ]);
1226
1227 $result = false;
1228 $errors = [];
1229
1230 $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
1231 if ($userenrolment) {
1232 $userid = $userenrolment->userid;
1233 $enrolid = $userenrolment->enrolid;
1234 $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
1235 $courseid = $enrol->courseid;
1236 $course = get_course($courseid);
1237 $context = context_course::instance($course->id);
1238 self::validate_context($context);
1239 } else {
1240 $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
1241 }
1242
1243 // If the userenrolment exists, unenrol the user.
1244 if (!isset($validationerrors)) {
1245 require_once($CFG->dirroot . '/enrol/locallib.php');
1246 $manager = new course_enrolment_manager($PAGE, $course);
1247 $result = $manager->unenrol_user($userenrolment);
1248 } else {
1249 foreach ($validationerrors as $key => $errormessage) {
1250 $errors[] = (object)[
1251 'key' => $key,
1252 'message' => $errormessage
1253 ];
1254 }
1255 }
1256
1257 return [
1258 'result' => $result,
1259 'errors' => $errors,
1260 ];
1261 }
1262
1263 /**
1264 * Returns description of unenrol_user_enrolment() result value
1265 *
1266 * @return external_description
1267 */
1268 public static function unenrol_user_enrolment_returns() {
58c20c81
JP
1269 return new external_single_structure(
1270 array(
1271 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1272 'errors' => new external_multiple_structure(
1273 new external_single_structure(
1274 array(
1275 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1276 'message' => new external_value(PARAM_TEXT, 'The error message'),
1277 )
1278 ), 'List of validation errors'
1279 ),
1280 )
1281 );
1282 }
5d1017e1
JM
1283}
1284
1285/**
4615817d
JM
1286 * Role external functions
1287 *
1288 * @package core_role
1289 * @category external
1290 * @copyright 2011 Jerome Mouneyrac
1291 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1292 * @since Moodle 2.2
5d1017e1
JM
1293 */
1294class core_role_external extends external_api {
1295
1296 /**
1297 * Returns description of method parameters
4615817d 1298 *
5d1017e1
JM
1299 * @return external_function_parameters
1300 */
1301 public static function assign_roles_parameters() {
1302 return new external_function_parameters(
1303 array(
1304 'assignments' => new external_multiple_structure(
1305 new external_single_structure(
1306 array(
1307 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1308 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
3a68f798
AA
1309 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1310 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1311 (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1312 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
5d1017e1
JM
1313 )
1314 )
1315 )
1316 )
1317 );
1318 }
1319
1320 /**
1321 * Manual role assignments to users
1322 *
4615817d 1323 * @param array $assignments An array of manual role assignment
5d1017e1
JM
1324 */
1325 public static function assign_roles($assignments) {
1326 global $DB;
1327
1328 // Do basic automatic PARAM checks on incoming data, using params description
1329 // If any problems are found then exceptions are thrown with helpful error messages
1330 $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1331
1332 $transaction = $DB->start_delegated_transaction();
1333
1334 foreach ($params['assignments'] as $assignment) {
3a68f798 1335 // Ensure correct context level with a instance id or contextid is passed.
5b23d9ad 1336 $context = self::get_context_from_params($assignment);
3a68f798
AA
1337
1338 // Ensure the current user is allowed to run this function in the enrolment context.
5d1017e1
JM
1339 self::validate_context($context);
1340 require_capability('moodle/role:assign', $context);
1341
1342 // throw an exception if user is not able to assign the role in this context
1343 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1344
12fc8acf 1345 if (!array_key_exists($assignment['roleid'], $roles)) {
5d1017e1
JM
1346 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1347 }
1348
3a68f798 1349 role_assign($assignment['roleid'], $assignment['userid'], $context->id);
5d1017e1
JM
1350 }
1351
1352 $transaction->allow_commit();
1353 }
1354
1355 /**
1356 * Returns description of method result value
4615817d
JM
1357 *
1358 * @return null
5d1017e1
JM
1359 */
1360 public static function assign_roles_returns() {
1361 return null;
1362 }
1363
1364
1365 /**
1366 * Returns description of method parameters
4615817d 1367 *
5d1017e1
JM
1368 * @return external_function_parameters
1369 */
1370 public static function unassign_roles_parameters() {
1371 return new external_function_parameters(
1372 array(
1373 'unassignments' => new external_multiple_structure(
1374 new external_single_structure(
1375 array(
1376 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'),
1377 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1e631792
AA
1378 'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1379 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1380+ (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1381 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
5d1017e1
JM
1382 )
1383 )
1384 )
1385 )
1386 );
1387 }
1388
1389 /**
1390 * Unassign roles from users
1391 *
4615817d 1392 * @param array $unassignments An array of unassignment
5d1017e1
JM
1393 */
1394 public static function unassign_roles($unassignments) {
1395 global $DB;
1396
1397 // Do basic automatic PARAM checks on incoming data, using params description
1398 // If any problems are found then exceptions are thrown with helpful error messages
1399 $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1400
1401 $transaction = $DB->start_delegated_transaction();
1402
1403 foreach ($params['unassignments'] as $unassignment) {
1404 // Ensure the current user is allowed to run this function in the unassignment context
1e631792 1405 $context = self::get_context_from_params($unassignment);
5d1017e1
JM
1406 self::validate_context($context);
1407 require_capability('moodle/role:assign', $context);
1408
1409 // throw an exception if user is not able to unassign the role in this context
1410 $roles = get_assignable_roles($context, ROLENAME_SHORT);
12fc8acf 1411 if (!array_key_exists($unassignment['roleid'], $roles)) {
5d1017e1
JM
1412 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1413 }
1414
1e631792 1415 role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
5d1017e1
JM
1416 }
1417
1418 $transaction->allow_commit();
1419 }
1420
1421 /**
1422 * Returns description of method result value
4615817d 1423 *
5d1017e1
JM
1424 * @return null
1425 */
1426 public static function unassign_roles_returns() {
1427 return null;
1428 }
1429}