Merge branch 'MDL-60738-master' of git://github.com/jleyva/moodle
[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);
169                 $sql = "SELECT u.* FROM {user} u WHERE u.id IN ($enrolledsql) ORDER BY u.id ASC";
171                 $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
172                 $users = array();
173                 foreach ($enrolledusers as $courseuser) {
174                     if ($userdetails = user_get_user_details($courseuser, $course, $userfields)) {
175                         $users[] = $userdetails;
176                     }
177                 }
178                 $enrolledusers->close();
179                 $courseusers['users'] = $users;
180                 $result[] = $courseusers;
181             }
182         }
183         return $result;
184     }
186     /**
187      * Returns description of method result value
188      *
189      * @return external_multiple_structure
190      * @since Moodle 2.4
191      */
192     public static function get_enrolled_users_with_capability_returns() {
193         return  new external_multiple_structure( new external_single_structure (
194                 array (
195                     'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'),
196                     'capability' => new external_value(PARAM_CAPABILITY, 'Capability name'),
197                     'users' => new external_multiple_structure(
198                         new external_single_structure(
199                 array(
200                     'id'    => new external_value(PARAM_INT, 'ID of the user'),
201                     'username'    => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL),
202                     'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
203                     'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
204                     'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
205                     'email'       => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL),
206                     'address'     => new external_value(PARAM_MULTILANG, 'Postal address', VALUE_OPTIONAL),
207                     'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
208                     'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
209                     'icq'         => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
210                     'skype'       => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
211                     'yahoo'       => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
212                     'aim'         => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
213                     'msn'         => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
214                     'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
215                     'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
216                     'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
217                     'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
218                     'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
219                     'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
220                     'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
221                     'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
222                     'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
223                     'country'     => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
224                     'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
225                     'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
226                     'customfields' => new external_multiple_structure(
227                         new external_single_structure(
228                             array(
229                                 'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
230                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
231                                 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
232                                 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
233                             )
234                         ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
235                     'groups' => new external_multiple_structure(
236                         new external_single_structure(
237                             array(
238                                 'id'  => new external_value(PARAM_INT, 'group id'),
239                                 'name' => new external_value(PARAM_RAW, 'group name'),
240                                 'description' => new external_value(PARAM_RAW, 'group description'),
241                             )
242                         ), 'user groups', VALUE_OPTIONAL),
243                     'roles' => new external_multiple_structure(
244                         new external_single_structure(
245                             array(
246                                 'roleid'       => new external_value(PARAM_INT, 'role id'),
247                                 'name'         => new external_value(PARAM_RAW, 'role name'),
248                                 'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
249                                 'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
250                             )
251                         ), 'user roles', VALUE_OPTIONAL),
252                     'preferences' => new external_multiple_structure(
253                         new external_single_structure(
254                             array(
255                                 'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
256                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
257                             )
258                     ), 'User preferences', VALUE_OPTIONAL),
259                     'enrolledcourses' => new external_multiple_structure(
260                         new external_single_structure(
261                             array(
262                                 'id'  => new external_value(PARAM_INT, 'Id of the course'),
263                                 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
264                                 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
265                             )
266                     ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
267                 )
268                         ), 'List of users that are enrolled in the course and have the specified capability'),
269                     )
270                 )
271             );
272     }
274     /**
275      * Returns description of method parameters
276      *
277      * @return external_function_parameters
278      */
279     public static function get_users_courses_parameters() {
280         return new external_function_parameters(
281             array(
282                 'userid' => new external_value(PARAM_INT, 'user id'),
283             )
284         );
285     }
287     /**
288      * Get list of courses user is enrolled in (only active enrolments are returned).
289      * Please note the current user must be able to access the course, otherwise the course is not included.
290      *
291      * @param int $userid
292      * @return array of courses
293      */
294     public static function get_users_courses($userid) {
295         global $CFG, $USER, $DB;
297         require_once($CFG->dirroot . '/course/lib.php');
299         // Do basic automatic PARAM checks on incoming data, using params description
300         // If any problems are found then exceptions are thrown with helpful error messages
301         $params = self::validate_parameters(self::get_users_courses_parameters(), array('userid'=>$userid));
303         $courses = enrol_get_users_courses($params['userid'], true, 'id, shortname, fullname, idnumber, visible,
304                    summary, summaryformat, format, showgrades, lang, enablecompletion, category, startdate, enddate');
305         $result = array();
307         foreach ($courses as $course) {
308             $context = context_course::instance($course->id, IGNORE_MISSING);
309             try {
310                 self::validate_context($context);
311             } catch (Exception $e) {
312                 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
313                 continue;
314             }
316             if ($userid != $USER->id and !course_can_view_participants($context)) {
317                 // we need capability to view participants
318                 continue;
319             }
321             list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
322             $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids";
323             $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
325             list($course->summary, $course->summaryformat) =
326                 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
327             $course->fullname = external_format_string($course->fullname, $context->id);
328             $course->shortname = external_format_string($course->shortname, $context->id);
330             $progress = null;
331             if ($course->enablecompletion) {
332                 $progress = \core_completion\progress::get_course_progress_percentage($course);
333             }
335             $result[] = array(
336                 'id' => $course->id,
337                 'shortname' => $course->shortname,
338                 'fullname' => $course->fullname,
339                 'idnumber' => $course->idnumber,
340                 'visible' => $course->visible,
341                 'enrolledusercount' => $enrolledusercount,
342                 'summary' => $course->summary,
343                 'summaryformat' => $course->summaryformat,
344                 'format' => $course->format,
345                 'showgrades' => $course->showgrades,
346                 'lang' => clean_param($course->lang, PARAM_LANG),
347                 'enablecompletion' => $course->enablecompletion,
348                 'category' => $course->category,
349                 'progress' => $progress,
350                 'startdate' => $course->startdate,
351                 'enddate' => $course->enddate,
352             );
353         }
355         return $result;
356     }
358     /**
359      * Returns description of method result value
360      *
361      * @return external_description
362      */
363     public static function get_users_courses_returns() {
364         return new external_multiple_structure(
365             new external_single_structure(
366                 array(
367                     'id'        => new external_value(PARAM_INT, 'id of course'),
368                     'shortname' => new external_value(PARAM_RAW, 'short name of course'),
369                     'fullname'  => new external_value(PARAM_RAW, 'long name of course'),
370                     'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course'),
371                     'idnumber'  => new external_value(PARAM_RAW, 'id number of course'),
372                     'visible'   => new external_value(PARAM_INT, '1 means visible, 0 means hidden course'),
373                     'summary'   => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
374                     'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
375                     'format'    => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site', VALUE_OPTIONAL),
376                     'showgrades' => new external_value(PARAM_BOOL, 'true if grades are shown, otherwise false', VALUE_OPTIONAL),
377                     'lang'      => new external_value(PARAM_LANG, 'forced course language', VALUE_OPTIONAL),
378                     'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false',
379                                                                 VALUE_OPTIONAL),
380                     'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
381                     'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
382                     'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
383                     'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
384                 )
385             )
386         );
387     }
389     /**
390      * Returns description of method parameters value
391      *
392      * @return external_description
393      */
394     public static function get_potential_users_parameters() {
395         return new external_function_parameters(
396             array(
397                 'courseid' => new external_value(PARAM_INT, 'course id'),
398                 'enrolid' => new external_value(PARAM_INT, 'enrolment id'),
399                 'search' => new external_value(PARAM_RAW, 'query'),
400                 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
401                 'page' => new external_value(PARAM_INT, 'Page number'),
402                 'perpage' => new external_value(PARAM_INT, 'Number per page'),
403             )
404         );
405     }
407     /**
408      * Get potential users.
409      *
410      * @param int $courseid Course id
411      * @param int $enrolid Enrolment id
412      * @param string $search The query
413      * @param boolean $searchanywhere Match anywhere in the string
414      * @param int $page Page number
415      * @param int $perpage Max per page
416      * @return array An array of users
417      */
418     public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) {
419         global $PAGE, $DB, $CFG;
421         require_once($CFG->dirroot.'/enrol/locallib.php');
422         require_once($CFG->dirroot.'/user/lib.php');
424         $params = self::validate_parameters(
425             self::get_potential_users_parameters(),
426             array(
427                 'courseid' => $courseid,
428                 'enrolid' => $enrolid,
429                 'search' => $search,
430                 'searchanywhere' => $searchanywhere,
431                 'page' => $page,
432                 'perpage' => $perpage
433             )
434         );
435         $context = context_course::instance($params['courseid']);
436         try {
437             self::validate_context($context);
438         } catch (Exception $e) {
439             $exceptionparam = new stdClass();
440             $exceptionparam->message = $e->getMessage();
441             $exceptionparam->courseid = $params['courseid'];
442             throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
443         }
444         require_capability('moodle/course:enrolreview', $context);
446         $course = $DB->get_record('course', array('id' => $params['courseid']));
447         $manager = new course_enrolment_manager($PAGE, $course);
449         $users = $manager->get_potential_users($params['enrolid'],
450                                                $params['search'],
451                                                $params['searchanywhere'],
452                                                $params['page'],
453                                                $params['perpage']);
455         $results = array();
456         // Add also extra user fields.
457         $requiredfields = array_merge(
458             ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
459             get_extra_user_fields($context)
460         );
461         foreach ($users['users'] as $id => $user) {
462             // Note: We pass the course here to validate that the current user can at least view user details in this course.
463             // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
464             // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
465             // there is no way to find users who aren't in the course in order to enrol them.
466             if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
467                 $results[] = $userdetails;
468             }
469         }
470         return $results;
471     }
473     /**
474      * Returns description of method result value
475      *
476      * @return external_description
477      */
478     public static function get_potential_users_returns() {
479         global $CFG;
480         require_once($CFG->dirroot . '/user/externallib.php');
481         return new external_multiple_structure(core_user_external::user_description());
482     }
484     /**
485      * Returns description of method parameters
486      *
487      * @return external_function_parameters
488      */
489     public static function get_enrolled_users_parameters() {
490         return new external_function_parameters(
491             array(
492                 'courseid' => new external_value(PARAM_INT, 'course id'),
493                 'options'  => new external_multiple_structure(
494                     new external_single_structure(
495                         array(
496                             'name'  => new external_value(PARAM_ALPHANUMEXT, 'option name'),
497                             'value' => new external_value(PARAM_RAW, 'option value')
498                         )
499                     ), 'Option names:
500                             * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
501                             * groupid (integer) return only users in this group id. If the course has groups enabled and this param
502                                                 isn\'t defined, returns all the viewable users.
503                                                 This option requires \'moodle/site:accessallgroups\' on the course context if the
504                                                 user doesn\'t belong to the group.
505                             * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
506                             * userfields (\'string, string, ...\') return only the values of these user fields.
507                             * limitfrom (integer) sql limit from.
508                             * limitnumber (integer) maximum number of returned users.
509                             * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
510                             * sortdirection (string) ASC or DESC',
511                             VALUE_DEFAULT, array()),
512             )
513         );
514     }
516     /**
517      * Get course participants details
518      *
519      * @param int $courseid  course id
520      * @param array $options options {
521      *                                'name' => option name
522      *                                'value' => option value
523      *                               }
524      * @return array An array of users
525      */
526     public static function get_enrolled_users($courseid, $options = array()) {
527         global $CFG, $USER, $DB;
529         require_once($CFG->dirroot . '/course/lib.php');
530         require_once($CFG->dirroot . "/user/lib.php");
532         $params = self::validate_parameters(
533             self::get_enrolled_users_parameters(),
534             array(
535                 'courseid'=>$courseid,
536                 'options'=>$options
537             )
538         );
539         $withcapability = '';
540         $groupid        = 0;
541         $onlyactive     = false;
542         $userfields     = array();
543         $limitfrom = 0;
544         $limitnumber = 0;
545         $sortby = 'us.id';
546         $sortparams = array();
547         $sortdirection = 'ASC';
548         foreach ($options as $option) {
549             switch ($option['name']) {
550             case 'withcapability':
551                 $withcapability = $option['value'];
552                 break;
553             case 'groupid':
554                 $groupid = (int)$option['value'];
555                 break;
556             case 'onlyactive':
557                 $onlyactive = !empty($option['value']);
558                 break;
559             case 'userfields':
560                 $thefields = explode(',', $option['value']);
561                 foreach ($thefields as $f) {
562                     $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
563                 }
564                 break;
565             case 'limitfrom' :
566                 $limitfrom = clean_param($option['value'], PARAM_INT);
567                 break;
568             case 'limitnumber' :
569                 $limitnumber = clean_param($option['value'], PARAM_INT);
570                 break;
571             case 'sortby':
572                 $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
573                 if (!in_array($option['value'], $sortallowedvalues)) {
574                     throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
575                         'allowed values are: ' . implode(',', $sortallowedvalues));
576                 }
577                 if ($option['value'] == 'siteorder') {
578                     list($sortby, $sortparams) = users_order_by_sql('us');
579                 } else {
580                     $sortby = 'us.' . $option['value'];
581                 }
582                 break;
583             case 'sortdirection':
584                 $sortdirection = strtoupper($option['value']);
585                 $directionallowedvalues = array('ASC', 'DESC');
586                 if (!in_array($sortdirection, $directionallowedvalues)) {
587                     throw new invalid_parameter_exception('Invalid value for sortdirection parameter
588                         (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
589                 }
590                 break;
591             }
592         }
594         $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
595         $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
596         if ($courseid == SITEID) {
597             $context = context_system::instance();
598         } else {
599             $context = $coursecontext;
600         }
601         try {
602             self::validate_context($context);
603         } catch (Exception $e) {
604             $exceptionparam = new stdClass();
605             $exceptionparam->message = $e->getMessage();
606             $exceptionparam->courseid = $params['courseid'];
607             throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
608         }
610         course_require_view_participants($context);
612         // to overwrite this parameter, you need role:review capability
613         if ($withcapability) {
614             require_capability('moodle/role:review', $coursecontext);
615         }
616         // need accessallgroups capability if you want to overwrite this option
617         if (!empty($groupid) && !groups_is_member($groupid)) {
618             require_capability('moodle/site:accessallgroups', $coursecontext);
619         }
620         // to overwrite this option, you need course:enrolereview permission
621         if ($onlyactive) {
622             require_capability('moodle/course:enrolreview', $coursecontext);
623         }
625         list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
626         $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
627         $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
628         $enrolledparams['contextlevel'] = CONTEXT_USER;
630         $groupjoin = '';
631         if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
632                 !has_capability('moodle/site:accessallgroups', $coursecontext)) {
633             // Filter by groups the user can view.
634             $usergroups = groups_get_user_groups($course->id);
635             if (!empty($usergroups['0'])) {
636                 list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
637                 $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
638                 $enrolledparams = array_merge($enrolledparams, $groupparams);
639             } else {
640                 // User doesn't belong to any group, so he can't see any user. Return an empty array.
641                 return array();
642             }
643         }
644         $sql = "SELECT us.*
645                   FROM {user} us
646                   JOIN (
647                       SELECT DISTINCT u.id $ctxselect
648                         FROM {user} u $ctxjoin $groupjoin
649                        WHERE u.id IN ($enrolledsql)
650                   ) q ON q.id = us.id
651                 ORDER BY $sortby $sortdirection";
652         $enrolledparams = array_merge($enrolledparams, $sortparams);
653         $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
654         $users = array();
655         foreach ($enrolledusers as $user) {
656             context_helper::preload_from_record($user);
657             if ($userdetails = user_get_user_details($user, $course, $userfields)) {
658                 $users[] = $userdetails;
659             }
660         }
661         $enrolledusers->close();
663         return $users;
664     }
666     /**
667      * Returns description of method result value
668      *
669      * @return external_description
670      */
671     public static function get_enrolled_users_returns() {
672         return new external_multiple_structure(
673             new external_single_structure(
674                 array(
675                     'id'    => new external_value(PARAM_INT, 'ID of the user'),
676                     'username'    => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
677                     'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
678                     'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
679                     'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
680                     'email'       => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
681                     'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
682                     'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
683                     'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
684                     'icq'         => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
685                     'skype'       => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
686                     'yahoo'       => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
687                     'aim'         => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
688                     'msn'         => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
689                     'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
690                     'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
691                     'idnumber'    => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
692                     'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
693                     'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
694                     'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
695                     'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
696                     'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
697                     'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
698                     'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
699                     'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
700                     'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
701                     'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
702                     'customfields' => new external_multiple_structure(
703                         new external_single_structure(
704                             array(
705                                 'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
706                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
707                                 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
708                                 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
709                             )
710                         ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
711                     'groups' => new external_multiple_structure(
712                         new external_single_structure(
713                             array(
714                                 'id'  => new external_value(PARAM_INT, 'group id'),
715                                 'name' => new external_value(PARAM_RAW, 'group name'),
716                                 'description' => new external_value(PARAM_RAW, 'group description'),
717                                 'descriptionformat' => new external_format_value('description'),
718                             )
719                         ), 'user groups', VALUE_OPTIONAL),
720                     'roles' => new external_multiple_structure(
721                         new external_single_structure(
722                             array(
723                                 'roleid'       => new external_value(PARAM_INT, 'role id'),
724                                 'name'         => new external_value(PARAM_RAW, 'role name'),
725                                 'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
726                                 'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
727                             )
728                         ), 'user roles', VALUE_OPTIONAL),
729                     'preferences' => new external_multiple_structure(
730                         new external_single_structure(
731                             array(
732                                 'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
733                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
734                             )
735                     ), 'User preferences', VALUE_OPTIONAL),
736                     'enrolledcourses' => new external_multiple_structure(
737                         new external_single_structure(
738                             array(
739                                 'id'  => new external_value(PARAM_INT, 'Id of the course'),
740                                 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
741                                 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
742                             )
743                     ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
744                 )
745             )
746         );
747     }
749     /**
750      * Returns description of get_course_enrolment_methods() parameters
751      *
752      * @return external_function_parameters
753      */
754     public static function get_course_enrolment_methods_parameters() {
755         return new external_function_parameters(
756             array(
757                 'courseid' => new external_value(PARAM_INT, 'Course id')
758             )
759         );
760     }
762     /**
763      * Get list of active course enrolment methods for current user.
764      *
765      * @param int $courseid
766      * @return array of course enrolment methods
767      * @throws moodle_exception
768      */
769     public static function get_course_enrolment_methods($courseid) {
770         global $DB;
772         $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
773         self::validate_context(context_system::instance());
775         $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
776         $context = context_course::instance($course->id);
777         if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
778             throw new moodle_exception('coursehidden');
779         }
781         $result = array();
782         $enrolinstances = enrol_get_instances($params['courseid'], true);
783         foreach ($enrolinstances as $enrolinstance) {
784             if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
785                 if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
786                     $result[] = (array) $instanceinfo;
787                 }
788             }
789         }
790         return $result;
791     }
793     /**
794      * Returns description of get_course_enrolment_methods() result value
795      *
796      * @return external_description
797      */
798     public static function get_course_enrolment_methods_returns() {
799         return new external_multiple_structure(
800             new external_single_structure(
801                 array(
802                     'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
803                     'courseid' => new external_value(PARAM_INT, 'id of course'),
804                     'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
805                     'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
806                     'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
807                     'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
808                 )
809             )
810         );
811     }
813     /**
814      * Returns description of edit_user_enrolment() parameters
815      *
816      * @return external_function_parameters
817      */
818     public static function edit_user_enrolment_parameters() {
819         return new external_function_parameters(
820             array(
821                 'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
822                 'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
823                 'status' => new external_value(PARAM_INT, 'Enrolment status'),
824                 'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
825                 'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
826             )
827         );
828     }
830     /**
831      * External function that updates a given user enrolment.
832      *
833      * @param int $courseid The course ID.
834      * @param int $ueid The user enrolment ID.
835      * @param int $status The enrolment status.
836      * @param int $timestart Enrolment start timestamp.
837      * @param int $timeend Enrolment end timestamp.
838      * @return array An array consisting of the processing result, errors and form output, if available.
839      */
840     public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
841         global $CFG, $DB, $PAGE;
843         $params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
844             'courseid' => $courseid,
845             'ueid' => $ueid,
846             'status' => $status,
847             'timestart' => $timestart,
848             'timeend' => $timeend,
849         ]);
851         $course = get_course($courseid);
852         $context = context_course::instance($course->id);
853         self::validate_context($context);
855         $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
856         $userenroldata = [
857             'status' => $params['status'],
858             'timestart' => $params['timestart'],
859             'timeend' => $params['timeend'],
860         ];
862         $result = false;
863         $errors = [];
865         // Validate data against the edit user enrolment form.
866         $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
867         $plugin = enrol_get_plugin($instance->enrol);
868         require_once("$CFG->dirroot/enrol/editenrolment_form.php");
869         $customformdata = [
870             'ue' => $userenrolment,
871             'modal' => true,
872             'enrolinstancename' => $plugin->get_instance_name($instance)
873         ];
874         $mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
875         $mform->set_data($userenroldata);
876         $validationerrors = $mform->validation($userenroldata, null);
877         if (empty($validationerrors)) {
878             require_once($CFG->dirroot . '/enrol/locallib.php');
879             $manager = new course_enrolment_manager($PAGE, $course);
880             $result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
881         } else {
882             foreach ($validationerrors as $key => $errormessage) {
883                 $errors[] = (object)[
884                     'key' => $key,
885                     'message' => $errormessage
886                 ];
887             }
888         }
890         return [
891             'result' => $result,
892             'errors' => $errors,
893         ];
894     }
896     /**
897      * Returns description of edit_user_enrolment() result value
898      *
899      * @return external_description
900      */
901     public static function edit_user_enrolment_returns() {
902         return new external_single_structure(
903             array(
904                 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
905                 'errors' => new external_multiple_structure(
906                     new external_single_structure(
907                         array(
908                             'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
909                             'message' => new external_value(PARAM_TEXT, 'The error message'),
910                         )
911                     ), 'List of validation errors'
912                 ),
913             )
914         );
915     }
917     /**
918      * Returns description of unenrol_user_enrolment() parameters
919      *
920      * @return external_function_parameters
921      */
922     public static function unenrol_user_enrolment_parameters() {
923         return new external_function_parameters(
924             array(
925                 'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
926             )
927         );
928     }
930     /**
931      * External function that unenrols a given user enrolment.
932      *
933      * @param int $ueid The user enrolment ID.
934      * @return array An array consisting of the processing result, errors.
935      */
936     public static function unenrol_user_enrolment($ueid) {
937         global $CFG, $DB, $PAGE;
939         $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
940             'ueid' => $ueid
941         ]);
943         $result = false;
944         $errors = [];
946         $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
947         if ($userenrolment) {
948             $userid = $userenrolment->userid;
949             $enrolid = $userenrolment->enrolid;
950             $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
951             $courseid = $enrol->courseid;
952             $course = get_course($courseid);
953             $context = context_course::instance($course->id);
954             self::validate_context($context);
955         } else {
956             $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
957         }
959         // If the userenrolment exists, unenrol the user.
960         if (!isset($validationerrors)) {
961             require_once($CFG->dirroot . '/enrol/locallib.php');
962             $manager = new course_enrolment_manager($PAGE, $course);
963             $result = $manager->unenrol_user($userenrolment);
964         } else {
965             foreach ($validationerrors as $key => $errormessage) {
966                 $errors[] = (object)[
967                     'key' => $key,
968                     'message' => $errormessage
969                 ];
970             }
971         }
973         return [
974             'result' => $result,
975             'errors' => $errors,
976         ];
977     }
979     /**
980      * Returns description of unenrol_user_enrolment() result value
981      *
982      * @return external_description
983      */
984     public static function unenrol_user_enrolment_returns() {
985         return new external_single_structure(
986             array(
987                 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
988                 'errors' => new external_multiple_structure(
989                     new external_single_structure(
990                         array(
991                             'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
992                             'message' => new external_value(PARAM_TEXT, 'The error message'),
993                         )
994                     ), 'List of validation errors'
995                 ),
996             )
997         );
998     }
1001 /**
1002  * Role external functions
1003  *
1004  * @package    core_role
1005  * @category   external
1006  * @copyright  2011 Jerome Mouneyrac
1007  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1008  * @since Moodle 2.2
1009  */
1010 class core_role_external extends external_api {
1012     /**
1013      * Returns description of method parameters
1014      *
1015      * @return external_function_parameters
1016      */
1017     public static function assign_roles_parameters() {
1018         return new external_function_parameters(
1019             array(
1020                 'assignments' => new external_multiple_structure(
1021                     new external_single_structure(
1022                         array(
1023                             'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
1024                             'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1025                             'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1026                             'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1027                                     (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1028                             'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
1029                         )
1030                     )
1031                 )
1032             )
1033         );
1034     }
1036     /**
1037      * Manual role assignments to users
1038      *
1039      * @param array $assignments An array of manual role assignment
1040      */
1041     public static function assign_roles($assignments) {
1042         global $DB;
1044         // Do basic automatic PARAM checks on incoming data, using params description
1045         // If any problems are found then exceptions are thrown with helpful error messages
1046         $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1048         $transaction = $DB->start_delegated_transaction();
1050         foreach ($params['assignments'] as $assignment) {
1051             // Ensure correct context level with a instance id or contextid is passed.
1052             $context = self::get_context_from_params($assignment);
1054             // Ensure the current user is allowed to run this function in the enrolment context.
1055             self::validate_context($context);
1056             require_capability('moodle/role:assign', $context);
1058             // throw an exception if user is not able to assign the role in this context
1059             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1061             if (!array_key_exists($assignment['roleid'], $roles)) {
1062                 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1063             }
1065             role_assign($assignment['roleid'], $assignment['userid'], $context->id);
1066         }
1068         $transaction->allow_commit();
1069     }
1071     /**
1072      * Returns description of method result value
1073      *
1074      * @return null
1075      */
1076     public static function assign_roles_returns() {
1077         return null;
1078     }
1081     /**
1082      * Returns description of method parameters
1083      *
1084      * @return external_function_parameters
1085      */
1086     public static function unassign_roles_parameters() {
1087         return new external_function_parameters(
1088             array(
1089                 'unassignments' => new external_multiple_structure(
1090                     new external_single_structure(
1091                         array(
1092                             'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
1093                             'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1094                             'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1095                             'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1096 +                                    (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1097                             'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
1098                         )
1099                     )
1100                 )
1101             )
1102         );
1103     }
1105      /**
1106      * Unassign roles from users
1107      *
1108      * @param array $unassignments An array of unassignment
1109      */
1110     public static function unassign_roles($unassignments) {
1111          global $DB;
1113         // Do basic automatic PARAM checks on incoming data, using params description
1114         // If any problems are found then exceptions are thrown with helpful error messages
1115         $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1117         $transaction = $DB->start_delegated_transaction();
1119         foreach ($params['unassignments'] as $unassignment) {
1120             // Ensure the current user is allowed to run this function in the unassignment context
1121             $context = self::get_context_from_params($unassignment);
1122             self::validate_context($context);
1123             require_capability('moodle/role:assign', $context);
1125             // throw an exception if user is not able to unassign the role in this context
1126             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1127             if (!array_key_exists($unassignment['roleid'], $roles)) {
1128                 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1129             }
1131             role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
1132         }
1134         $transaction->allow_commit();
1135     }
1137    /**
1138      * Returns description of method result value
1139      *
1140      * @return null
1141      */
1142     public static function unassign_roles_returns() {
1143         return null;
1144     }