5189a3a7edda25c0e07a689a4573ee9c9d73afe8
[moodle.git] / enrol / externallib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
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/>.
18 /**
19  * External course participation api.
20  *
21  * This api is mostly read only, the actual enrol and unenrol
22  * support is in each enrol plugin.
23  *
24  * @package    core_enrol
25  * @category   external
26  * @copyright  2010 Jerome Mouneyrac
27  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28  */
30 defined('MOODLE_INTERNAL') || die();
32 require_once("$CFG->libdir/externallib.php");
34 /**
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
42  */
43 class core_enrol_external extends external_api {
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     }
79     /**
80      * Return users that have the capabilities for each course specified. For each course and capability specified,
81      * a list of the users that are enrolled in the course and have that capability are returned.
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;
90         require_once($CFG->dirroot . '/course/lib.php');
91         require_once($CFG->dirroot . "/user/lib.php");
93         if (empty($coursecapabilities)) {
94             throw new invalid_parameter_exception('Parameter can not be empty');
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                     }
118                     break;
119                 case 'limitfrom' :
120                     $limitfrom = clean_param($option['value'], PARAM_INT);
121                     break;
122                 case 'limitnumber' :
123                     $limitnumber = clean_param($option['value'], PARAM_INT);
124                     break;
125             }
126         }
128         foreach ($params['coursecapabilities'] as $coursecapability) {
129             $courseid = $coursecapability['courseid'];
130             $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
131             $coursecontext = context_course::instance($courseid);
132             if (!$coursecontext) {
133                 throw new moodle_exception('cannotfindcourse', 'error', '', null,
134                         'The course id ' . $courseid . ' doesn\'t exist.');
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             }
150             course_require_view_participants($context);
152             // The accessallgroups capability is needed to use this option.
153             if (!empty($groupid) && groups_is_member($groupid)) {
154                 require_capability('moodle/site:accessallgroups', $coursecontext);
155             }
156             // The course:enrolereview capability is needed to use this option.
157             if ($onlyactive) {
158                 require_capability('moodle/course:enrolreview', $coursecontext);
159             }
161             // To see the permissions of others role:review capability is required.
162             require_capability('moodle/role:review', $coursecontext);
163             foreach ($coursecapability['capabilities'] as $capability) {
164                 $courseusers['courseid'] = $courseid;
165                 $courseusers['capability'] = $capability;
167                 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $capability, $groupid, $onlyactive);
168                 $enrolledparams['courseid'] = $courseid;
170                 $sql = "SELECT u.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
171                           FROM {user} u
172                      LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = :courseid)
173                          WHERE u.id IN ($enrolledsql)
174                       ORDER BY u.id ASC";
176                 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
177                 $users = array();
178                 foreach ($enrolledusers as $courseuser) {
179                     if ($userdetails = user_get_user_details($courseuser, $course, $userfields)) {
180                         $users[] = $userdetails;
181                     }
182                 }
183                 $enrolledusers->close();
184                 $courseusers['users'] = $users;
185                 $result[] = $courseusers;
186             }
187         }
188         return $result;
189     }
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(
205                     'id'    => new external_value(PARAM_INT, 'ID of the user'),
206                     'username'    => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL),
207                     'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
208                     'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
209                     'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
210                     'email'       => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL),
211                     'address'     => new external_value(PARAM_MULTILANG, 'Postal address', VALUE_OPTIONAL),
212                     'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
213                     'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
214                     'icq'         => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
215                     'skype'       => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
216                     'yahoo'       => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
217                     'aim'         => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
218                     'msn'         => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
219                     'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
220                     'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
221                     'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
222                     'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
223                     'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
224                     'lastcourseaccess'  => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
225                     'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
226                     'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
227                     'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
228                     'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
229                     'country'     => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
230                     'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
231                     'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
232                     'customfields' => new external_multiple_structure(
233                         new external_single_structure(
234                             array(
235                                 'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
236                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
237                                 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
238                                 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
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(
261                                 'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
262                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
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     }
280     /**
281      * Returns description of method parameters
282      *
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'),
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),
294             )
295         );
296     }
298     /**
299      * Get list of courses user is enrolled in (only active enrolments are returned).
300      * Please note the current user must be able to access the course, otherwise the course is not included.
301      *
302      * @param int $userid
303      * @param bool $returnusercount
304      * @return array of courses
305      */
306     public static function get_users_courses($userid, $returnusercount = true) {
307         global $CFG, $USER, $DB;
309         require_once($CFG->dirroot . '/course/lib.php');
310         require_once($CFG->libdir . '/completionlib.php');
312         // Do basic automatic PARAM checks on incoming data, using params description
313         // If any problems are found then exceptions are thrown with helpful error messages
314         $params = self::validate_parameters(self::get_users_courses_parameters(),
315                 ['userid' => $userid, 'returnusercount' => $returnusercount]);
316         $userid = $params['userid'];
317         $returnusercount = $params['returnusercount'];
319         $courses = enrol_get_users_courses($userid, true, '*');
320         $result = array();
322         // Get user data including last access to courses.
323         $user = get_complete_user_data('id', $userid);
324         $sameuser = $USER->id == $userid;
326         // Retrieve favourited courses (starred).
327         $favouritecourseids = array();
328         if ($sameuser) {
329             $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
330             $favourites = $ufservice->find_favourites_by_type('core_course', 'courses');
332             if ($favourites) {
333                 $favouritecourseids = array_flip(array_map(
334                     function($favourite) {
335                         return $favourite->itemid;
336                     }, $favourites));
337             }
338         }
340         foreach ($courses as $course) {
341             $context = context_course::instance($course->id, IGNORE_MISSING);
342             try {
343                 self::validate_context($context);
344             } catch (Exception $e) {
345                 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
346                 continue;
347             }
349             if (!$sameuser and !course_can_view_participants($context)) {
350                 // we need capability to view participants
351                 continue;
352             }
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             }
360             $displayname = external_format_string(get_course_display_name_for_list($course), $context->id);
361             list($course->summary, $course->summaryformat) =
362                 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
363             $course->fullname = external_format_string($course->fullname, $context->id);
364             $course->shortname = external_format_string($course->shortname, $context->id);
366             $progress = null;
367             $completed = null;
368             $completionhascriteria = false;
369             $completionusertracked = false;
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);
376                     $completionhascriteria = $completion->has_criteria();
377                     $completionusertracked = $completion->is_tracked_user($userid);
378                     $progress = \core_completion\progress::get_course_progress_percentage($course, $userid);
379                 }
380             }
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             }
390             if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) {
391                 $lastaccess = $user->lastcourseaccess[$course->id];
392             }
394             $hidden = false;
395             if ($sameuser) {
396                 $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0));
397             }
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                 );
414             }
416             $courseresult = [
417                 'id' => $course->id,
418                 'shortname' => $course->shortname,
419                 'fullname' => $course->fullname,
420                 'displayname' => $displayname,
421                 'idnumber' => $course->idnumber,
422                 'visible' => $course->visible,
423                 'summary' => $course->summary,
424                 'summaryformat' => $course->summaryformat,
425                 'format' => $course->format,
426                 'showgrades' => $course->showgrades,
427                 'lang' => clean_param($course->lang, PARAM_LANG),
428                 'enablecompletion' => $course->enablecompletion,
429                 'completionhascriteria' => $completionhascriteria,
430                 'completionusertracked' => $completionusertracked,
431                 'category' => $course->category,
432                 'progress' => $progress,
433                 'completed' => $completed,
434                 'startdate' => $course->startdate,
435                 'enddate' => $course->enddate,
436                 'marker' => $course->marker,
437                 'lastaccess' => $lastaccess,
438                 'isfavourite' => isset($favouritecourseids[$course->id]),
439                 'hidden' => $hidden,
440                 'overviewfiles' => $overviewfiles,
441             ];
442             if ($returnusercount) {
443                 $courseresult['enrolledusercount'] = $enrolledusercount;
444             }
445             $result[] = $courseresult;
446         }
448         return $result;
449     }
451     /**
452      * Returns description of method result value
453      *
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'),
463                     'displayname' => new external_value(PARAM_RAW, 'course display name for lists.', VALUE_OPTIONAL),
464                     'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course',
465                             VALUE_OPTIONAL),
466                     'idnumber'  => new external_value(PARAM_RAW, 'id number of course'),
467                     'visible'   => new external_value(PARAM_INT, '1 means visible, 0 means not yet visible course'),
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),
473                     'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false',
474                                                                 VALUE_OPTIONAL),
475                     'completionhascriteria' => new external_value(PARAM_BOOL, 'If completion criteria is set.', VALUE_OPTIONAL),
476                     'completionusertracked' => new external_value(PARAM_BOOL, 'If the user is completion tracked.', VALUE_OPTIONAL),
477                     'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
478                     'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
479                     'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL),
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),
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),
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),
486                     'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL),
487                 )
488             )
489         );
490     }
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     }
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) {
522         global $PAGE, $DB, $CFG;
524         require_once($CFG->dirroot.'/enrol/locallib.php');
525         require_once($CFG->dirroot.'/user/lib.php');
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']);
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         }
547         require_capability('moodle/course:enrolreview', $context);
549         $course = $DB->get_record('course', array('id' => $params['courseid']));
550         $manager = new course_enrolment_manager($PAGE, $course);
552         $users = $manager->get_potential_users($params['enrolid'],
553                                                $params['search'],
554                                                $params['searchanywhere'],
555                                                $params['page'],
556                                                $params['perpage']);
558         $results = array();
559         // Add also extra user fields.
560         $requiredfields = array_merge(
561             ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
562             get_extra_user_fields($context)
563         );
564         foreach ($users['users'] as $id => $user) {
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.
569             if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
570                 $results[] = $userdetails;
571             }
572         }
573         return $results;
574     }
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     }
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     }
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;
618         require_once($CFG->dirroot.'/enrol/locallib.php');
619         require_once($CFG->dirroot.'/user/lib.php');
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);
642         $course = get_course($params['courseid']);
643         $manager = new course_enrolment_manager($PAGE, $course);
645         $users = $manager->search_users($params['search'],
646                                         $params['searchanywhere'],
647                                         $params['page'],
648                                         $params['perpage']);
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             if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
658                 $results[] = $userdetails;
659             }
660         }
661         return $results;
662     }
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     }
675     /**
676      * Returns description of method parameters
677      *
678      * @return external_function_parameters
679      */
680     public static function get_enrolled_users_parameters() {
681         return new external_function_parameters(
682             [
683                 'courseid' => new external_value(PARAM_INT, 'course id'),
684                 'options'  => new external_multiple_structure(
685                     new external_single_structure(
686                         [
687                             'name'  => new external_value(PARAM_ALPHANUMEXT, 'option name'),
688                             'value' => new external_value(PARAM_RAW, 'option value')
689                         ]
690                     ), 'Option names:
691                             * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
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.
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).
703                             * userfields (\'string, string, ...\') return only the values of these user fields.
704                             * limitfrom (integer) sql limit from.
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',
708                             VALUE_DEFAULT, []),
709             ]
710         );
711     }
713     /**
714      * Get course participants details
715      *
716      * @param int $courseid  course id
717      * @param array $options options {
718      *                                'name' => option name
719      *                                'value' => option value
720      *                               }
721      * @return array An array of users
722      */
723     public static function get_enrolled_users($courseid, $options = []) {
724         global $CFG, $USER, $DB;
726         require_once($CFG->dirroot . '/course/lib.php');
727         require_once($CFG->dirroot . "/user/lib.php");
729         $params = self::validate_parameters(
730             self::get_enrolled_users_parameters(),
731             [
732                 'courseid'=>$courseid,
733                 'options'=>$options
734             ]
735         );
736         $withcapability = '';
737         $groupid        = 0;
738         $onlyactive     = false;
739         $onlysuspended  = false;
740         $userfields     = [];
741         $limitfrom = 0;
742         $limitnumber = 0;
743         $sortby = 'us.id';
744         $sortparams = [];
745         $sortdirection = 'ASC';
746         foreach ($options as $option) {
747             switch ($option['name']) {
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
789                         (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
790                     }
791                     break;
792             }
793         }
795         $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
796         $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
797         if ($courseid == SITEID) {
798             $context = context_system::instance();
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'];
808             throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
809         }
811         course_require_view_participants($context);
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
818         if (!empty($groupid) && !groups_is_member($groupid)) {
819             require_capability('moodle/site:accessallgroups', $coursecontext);
820         }
821         // to overwrite this option, you need course:enrolereview permission
822         if ($onlyactive || $onlysuspended) {
823             require_capability('moodle/course:enrolreview', $coursecontext);
824         }
826         list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
827         $onlysuspended);
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;
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.
843                 return [];
844             }
845         }
846         $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
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
853              LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
854                 ORDER BY $sortby $sortdirection";
855         $enrolledparams = array_merge($enrolledparams, $sortparams);
856         $enrolledparams['courseid'] = $courseid;
858         $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
859         $users = [];
860         foreach ($enrolledusers as $user) {
861             context_helper::preload_from_record($user);
862             if ($userdetails = user_get_user_details($user, $course, $userfields)) {
863                 $users[] = $userdetails;
864             }
865         }
866         $enrolledusers->close();
868         return $users;
869     }
871     /**
872      * Returns description of method result value
873      *
874      * @return external_description
875      */
876     public static function get_enrolled_users_returns() {
877         return new external_multiple_structure(
878             new external_single_structure(
879                 [
880                     'id'    => new external_value(PARAM_INT, 'ID of the user'),
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),
886                     'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
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),
896                     'idnumber'    => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
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),
900                     'lastcourseaccess'  => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
901                     'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
902                     'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
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),
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),
908                     'customfields' => new external_multiple_structure(
909                         new external_single_structure(
910                             [
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'),
915                             ]
916                         ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
917                     'groups' => new external_multiple_structure(
918                         new external_single_structure(
919                             [
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'),
923                                 'descriptionformat' => new external_format_value('description'),
924                             ]
925                         ), 'user groups', VALUE_OPTIONAL),
926                     'roles' => new external_multiple_structure(
927                         new external_single_structure(
928                             [
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')
933                             ]
934                         ), 'user roles', VALUE_OPTIONAL),
935                     'preferences' => new external_multiple_structure(
936                         new external_single_structure(
937                             [
938                                 'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
939                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
940                             ]
941                     ), 'User preferences', VALUE_OPTIONAL),
942                     'enrolledcourses' => new external_multiple_structure(
943                         new external_single_structure(
944                             [
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')
948                             ]
949                     ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
950                 ]
951             )
952         );
953     }
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     }
968     /**
969      * Get list of active course enrolment methods for current user.
970      *
971      * @param int $courseid
972      * @return array of course enrolment methods
973      * @throws moodle_exception
974      */
975     public static function get_course_enrolment_methods($courseid) {
976         global $DB;
978         $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
979         self::validate_context(context_system::instance());
981         $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
982         if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
983             throw new moodle_exception('coursehidden');
984         }
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     }
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     }
1018     /**
1019      * Returns description of edit_user_enrolment() parameters
1020      *
1021      * @deprecated since 3.8
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     }
1036     /**
1037      * External function that updates a given user enrolment.
1038      *
1039      * @deprecated since 3.8
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;
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         ]);
1058         $course = get_course($courseid);
1059         $context = context_course::instance($course->id);
1060         self::validate_context($context);
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         ];
1069         $result = false;
1070         $errors = [];
1072         // Validate data against the edit user enrolment form.
1073         $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1074         $plugin = enrol_get_plugin($instance->enrol);
1075         require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1076         $customformdata = [
1077             'ue' => $userenrolment,
1078             'modal' => true,
1079             'enrolinstancename' => $plugin->get_instance_name($instance)
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         }
1097         return [
1098             'result' => $result,
1099             'errors' => $errors,
1100         ];
1101     }
1103     /**
1104      * Returns description of edit_user_enrolment() result value
1105      *
1106      * @deprecated since 3.8
1107      * @return external_description
1108      */
1109     public static function edit_user_enrolment_returns() {
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     }
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     }
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     }
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;
1154         // Parameter validation.
1155         $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
1157         $data = [];
1158         parse_str($params['formdata'], $data);
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);
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);
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);
1183             return ['result' => $result];
1184         } else {
1185             return ['result' => false, 'validationerror' => true];
1186         }
1187     }
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     }
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     }
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;
1223         $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1224             'ueid' => $ueid
1225         ]);
1227         $result = false;
1228         $errors = [];
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         }
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         }
1257         return [
1258             'result' => $result,
1259             'errors' => $errors,
1260         ];
1261     }
1263     /**
1264      * Returns description of unenrol_user_enrolment() result value
1265      *
1266      * @return external_description
1267      */
1268     public static function unenrol_user_enrolment_returns() {
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     }
1285 /**
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
1293  */
1294 class core_role_external extends external_api {
1296     /**
1297      * Returns description of method parameters
1298      *
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'),
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),
1313                         )
1314                     )
1315                 )
1316             )
1317         );
1318     }
1320     /**
1321      * Manual role assignments to users
1322      *
1323      * @param array $assignments An array of manual role assignment
1324      */
1325     public static function assign_roles($assignments) {
1326         global $DB;
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));
1332         $transaction = $DB->start_delegated_transaction();
1334         foreach ($params['assignments'] as $assignment) {
1335             // Ensure correct context level with a instance id or contextid is passed.
1336             $context = self::get_context_from_params($assignment);
1338             // Ensure the current user is allowed to run this function in the enrolment context.
1339             self::validate_context($context);
1340             require_capability('moodle/role:assign', $context);
1342             // throw an exception if user is not able to assign the role in this context
1343             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1345             if (!array_key_exists($assignment['roleid'], $roles)) {
1346                 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1347             }
1349             role_assign($assignment['roleid'], $assignment['userid'], $context->id);
1350         }
1352         $transaction->allow_commit();
1353     }
1355     /**
1356      * Returns description of method result value
1357      *
1358      * @return null
1359      */
1360     public static function assign_roles_returns() {
1361         return null;
1362     }
1365     /**
1366      * Returns description of method parameters
1367      *
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'),
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),
1382                         )
1383                     )
1384                 )
1385             )
1386         );
1387     }
1389      /**
1390      * Unassign roles from users
1391      *
1392      * @param array $unassignments An array of unassignment
1393      */
1394     public static function unassign_roles($unassignments) {
1395          global $DB;
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));
1401         $transaction = $DB->start_delegated_transaction();
1403         foreach ($params['unassignments'] as $unassignment) {
1404             // Ensure the current user is allowed to run this function in the unassignment context
1405             $context = self::get_context_from_params($unassignment);
1406             self::validate_context($context);
1407             require_capability('moodle/role:assign', $context);
1409             // throw an exception if user is not able to unassign the role in this context
1410             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1411             if (!array_key_exists($unassignment['roleid'], $roles)) {
1412                 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1413             }
1415             role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
1416         }
1418         $transaction->allow_commit();
1419     }
1421    /**
1422      * Returns description of method result value
1423      *
1424      * @return null
1425      */
1426     public static function unassign_roles_returns() {
1427         return null;
1428     }