Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/>.
16
 
17
/**
18
 * Course participations External functions.
19
 *
20
 * @package    core_enrol
21
 * @category   external
22
 * @copyright  2010 Jerome Mouneyrac
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
use core_external\external_api;
27
use core_external\external_files;
28
use core_external\external_format_value;
29
use core_external\external_function_parameters;
30
use core_external\external_multiple_structure;
31
use core_external\external_single_structure;
32
use core_external\external_value;
33
 
34
/**
35
 * Enrol external functions
36
 *
37
 * This api is mostly read only, the actual enrol and unenrol
38
 * support is in each enrol plugin.
39
 *
40
 * @package    core_enrol
41
 * @category   external
42
 * @copyright  2010 Jerome Mouneyrac
43
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44
 * @since Moodle 2.2
45
 */
46
class core_enrol_external extends external_api {
47
 
48
    /**
49
     * Returns description of method parameters
50
     *
51
     * @return external_function_parameters
52
     * @since Moodle 2.4
53
     */
54
    public static function get_enrolled_users_with_capability_parameters() {
55
        return new external_function_parameters(
56
            array (
57
                'coursecapabilities' => new external_multiple_structure(
58
                    new external_single_structure(
59
                        array (
60
                            'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'),
61
                            'capabilities' => new external_multiple_structure(
62
                                new external_value(PARAM_CAPABILITY, 'Capability name, such as mod/forum:viewdiscussion')),
63
                        )
64
                    )
65
                , 'course id and associated capability name'),
66
                 'options'  => new external_multiple_structure(
67
                    new external_single_structure(
68
                        array(
69
                            'name'  => new external_value(PARAM_ALPHANUMEXT, 'option name'),
70
                            'value' => new external_value(PARAM_RAW, 'option value')
71
                        )
72
                    ), 'Option names:
73
                            * groupid (integer) return only users in this group id. Requires \'moodle/site:accessallgroups\' .
74
                            * onlyactive (integer) only users with active enrolments. Requires \'moodle/course:enrolreview\' .
75
                            * userfields (\'string, string, ...\') return only the values of these user fields.
76
                            * limitfrom (integer) sql limit from.
77
                            * limitnumber (integer) max number of users per course and capability.', VALUE_DEFAULT, array())
78
            )
79
        );
80
    }
81
 
82
    /**
83
     * Return users that have the capabilities for each course specified. For each course and capability specified,
84
     * a list of the users that are enrolled in the course and have that capability are returned.
85
     *
86
     * @param array $coursecapabilities array of course ids and associated capability names {courseid, {capabilities}}
87
     * @return array An array of arrays describing users for each associated courseid and capability
88
     * @since  Moodle 2.4
89
     */
90
    public static function get_enrolled_users_with_capability($coursecapabilities, $options) {
91
        global $CFG, $DB;
92
 
93
        require_once($CFG->dirroot . '/course/lib.php');
94
        require_once($CFG->dirroot . "/user/lib.php");
95
 
96
        if (empty($coursecapabilities)) {
97
            throw new invalid_parameter_exception('Parameter can not be empty');
98
        }
99
        $params = self::validate_parameters(self::get_enrolled_users_with_capability_parameters(),
100
            array ('coursecapabilities' => $coursecapabilities,  'options'=>$options));
101
        $result = array();
102
        $userlist = array();
103
        $groupid        = 0;
104
        $onlyactive     = false;
105
        $userfields     = array();
106
        $limitfrom = 0;
107
        $limitnumber = 0;
108
        foreach ($params['options'] as $option) {
109
            switch ($option['name']) {
110
                case 'groupid':
111
                    $groupid = (int)$option['value'];
112
                    break;
113
                case 'onlyactive':
114
                    $onlyactive = !empty($option['value']);
115
                    break;
116
                case 'userfields':
117
                    $thefields = explode(',', $option['value']);
118
                    foreach ($thefields as $f) {
119
                        $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
120
                    }
121
                    break;
122
                case 'limitfrom' :
123
                    $limitfrom = clean_param($option['value'], PARAM_INT);
124
                    break;
125
                case 'limitnumber' :
126
                    $limitnumber = clean_param($option['value'], PARAM_INT);
127
                    break;
128
            }
129
        }
130
 
131
        foreach ($params['coursecapabilities'] as $coursecapability) {
132
            $courseid = $coursecapability['courseid'];
133
            $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
134
            $coursecontext = context_course::instance($courseid);
135
            if (!$coursecontext) {
136
                throw new moodle_exception('cannotfindcourse', 'error', '', null,
137
                        'The course id ' . $courseid . ' doesn\'t exist.');
138
            }
139
            if ($courseid == SITEID) {
140
                $context = context_system::instance();
141
            } else {
142
                $context = $coursecontext;
143
            }
144
            try {
145
                self::validate_context($context);
146
            } catch (Exception $e) {
147
                $exceptionparam = new stdClass();
148
                $exceptionparam->message = $e->getMessage();
149
                $exceptionparam->courseid = $params['courseid'];
150
                throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
151
            }
152
 
153
            course_require_view_participants($context);
154
 
155
            // The accessallgroups capability is needed to use this option.
156
            if (!empty($groupid) && groups_is_member($groupid)) {
157
                require_capability('moodle/site:accessallgroups', $coursecontext);
158
            }
159
            // The course:enrolereview capability is needed to use this option.
160
            if ($onlyactive) {
161
                require_capability('moodle/course:enrolreview', $coursecontext);
162
            }
163
 
164
            // To see the permissions of others role:review capability is required.
165
            require_capability('moodle/role:review', $coursecontext);
166
            foreach ($coursecapability['capabilities'] as $capability) {
167
                $courseusers['courseid'] = $courseid;
168
                $courseusers['capability'] = $capability;
169
 
170
                list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $capability, $groupid, $onlyactive);
171
                $enrolledparams['courseid'] = $courseid;
172
 
173
                $sql = "SELECT u.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
174
                          FROM {user} u
175
                     LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = :courseid)
176
                         WHERE u.id IN ($enrolledsql)
177
                      ORDER BY u.id ASC";
178
 
179
                $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
180
                $users = array();
181
                foreach ($enrolledusers as $courseuser) {
182
                    if ($userdetails = user_get_user_details($courseuser, $course, $userfields)) {
183
                        $users[] = $userdetails;
184
                    }
185
                }
186
                $enrolledusers->close();
187
                $courseusers['users'] = $users;
188
                $result[] = $courseusers;
189
            }
190
        }
191
        return $result;
192
    }
193
 
194
    /**
195
     * Returns description of method result value
196
     *
197
     * @return external_multiple_structure
198
     * @since Moodle 2.4
199
     */
200
    public static function get_enrolled_users_with_capability_returns() {
201
        return  new external_multiple_structure( new external_single_structure (
202
                array (
203
                    'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'),
204
                    'capability' => new external_value(PARAM_CAPABILITY, 'Capability name'),
205
                    'users' => new external_multiple_structure(
206
                        new external_single_structure(
207
                array(
208
                    'id'    => new external_value(PARAM_INT, 'ID of the user'),
209
                    'username'    => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL),
210
                    'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
211
                    'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
212
                    'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
213
                    'email'       => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL),
214
                    'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
215
                    'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
216
                    'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
217
                    'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
218
                    'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
219
                    'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
220
                    'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
221
                    'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
222
                    'lastcourseaccess'  => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
223
                    'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
224
                    'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
225
                    'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
226
                    'country'     => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
227
                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
228
                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
229
                    'customfields' => new external_multiple_structure(
230
                        new external_single_structure(
231
                            array(
232
                                'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
233
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
234
                                'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
235
                                'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
236
                            )
237
                        ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
238
                    'groups' => new external_multiple_structure(
239
                        new external_single_structure(
240
                            array(
241
                                'id'  => new external_value(PARAM_INT, 'group id'),
242
                                'name' => new external_value(PARAM_RAW, 'group name'),
243
                                'description' => new external_value(PARAM_RAW, 'group description'),
244
                            )
245
                        ), 'user groups', VALUE_OPTIONAL),
246
                    'roles' => new external_multiple_structure(
247
                        new external_single_structure(
248
                            array(
249
                                'roleid'       => new external_value(PARAM_INT, 'role id'),
250
                                'name'         => new external_value(PARAM_RAW, 'role name'),
251
                                'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
252
                                'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
253
                            )
254
                        ), 'user roles', VALUE_OPTIONAL),
255
                    'preferences' => new external_multiple_structure(
256
                        new external_single_structure(
257
                            array(
258
                                'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
259
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
260
                            )
261
                    ), 'User preferences', VALUE_OPTIONAL),
262
                    'enrolledcourses' => new external_multiple_structure(
263
                        new external_single_structure(
264
                            array(
265
                                'id'  => new external_value(PARAM_INT, 'Id of the course'),
266
                                'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
267
                                'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
268
                            )
269
                    ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
270
                )
271
                        ), 'List of users that are enrolled in the course and have the specified capability'),
272
                    )
273
                )
274
            );
275
    }
276
 
277
    /**
278
     * Returns description of method parameters
279
     *
280
     * @return external_function_parameters
281
     */
282
    public static function get_users_courses_parameters() {
283
        return new external_function_parameters(
284
            array(
285
                'userid' => new external_value(PARAM_INT, 'user id'),
286
                'returnusercount' => new external_value(PARAM_BOOL,
287
                        'Include count of enrolled users for each course? This can add several seconds to the response time'
288
                            . ' if a user is on several large courses, so set this to false if the value will not be used to'
289
                            . ' improve performance.',
290
                        VALUE_DEFAULT, true),
291
            )
292
        );
293
    }
294
 
295
    /**
296
     * Get list of courses user is enrolled in (only active enrolments are returned).
297
     * Please note the current user must be able to access the course, otherwise the course is not included.
298
     *
299
     * @param int $userid
300
     * @param bool $returnusercount
301
     * @return array of courses
302
     */
303
    public static function get_users_courses($userid, $returnusercount = true) {
304
        global $CFG, $USER, $DB, $OUTPUT;
305
 
306
        require_once($CFG->dirroot . '/course/lib.php');
307
        require_once($CFG->dirroot . '/user/lib.php');
308
        require_once($CFG->libdir . '/completionlib.php');
309
 
310
        // Do basic automatic PARAM checks on incoming data, using params description
311
        // If any problems are found then exceptions are thrown with helpful error messages
312
        $params = self::validate_parameters(self::get_users_courses_parameters(),
313
                ['userid' => $userid, 'returnusercount' => $returnusercount]);
314
        $userid = $params['userid'];
315
        $returnusercount = $params['returnusercount'];
316
 
317
        $courses = enrol_get_users_courses($userid, true, '*');
318
        $result = array();
319
 
320
        // Get user data including last access to courses.
321
        $user = get_complete_user_data('id', $userid);
322
        $sameuser = $USER->id == $userid;
323
 
324
        // Retrieve favourited courses (starred).
325
        $favouritecourseids = array();
326
        if ($sameuser) {
327
            $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
328
            $favourites = $ufservice->find_favourites_by_type('core_course', 'courses');
329
 
330
            if ($favourites) {
331
                $favouritecourseids = array_flip(array_map(
332
                    function($favourite) {
333
                        return $favourite->itemid;
334
                    }, $favourites));
335
            }
336
        }
337
 
338
        foreach ($courses as $course) {
339
            $context = context_course::instance($course->id, IGNORE_MISSING);
340
            try {
341
                self::validate_context($context);
342
            } catch (Exception $e) {
343
                // current user can not access this course, sorry we can not disclose who is enrolled in this course!
344
                continue;
345
            }
346
 
347
            // If viewing details of another user, then we must be able to view participants as well as profile of that user.
348
            if (!$sameuser && (!course_can_view_participants($context) || !user_can_view_profile($user, $course))) {
349
                continue;
350
            }
351
 
352
            if ($returnusercount) {
353
                list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
354
                $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids";
355
                $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
356
            }
357
 
358
            $displayname = \core_external\util::format_string(get_course_display_name_for_list($course), $context);
359
            list($course->summary, $course->summaryformat) =
360
                \core_external\util::format_text($course->summary, $course->summaryformat, $context, 'course', 'summary', null);
361
            $course->fullname = \core_external\util::format_string($course->fullname, $context);
362
            $course->shortname = \core_external\util::format_string($course->shortname, $context);
363
 
364
            $progress = null;
365
            $completed = null;
366
            $completionhascriteria = false;
367
            $completionusertracked = false;
368
 
369
            // Return only private information if the user should be able to see it.
370
            if ($sameuser || completion_can_view_data($userid, $course)) {
371
                if ($course->enablecompletion) {
372
                    $completion = new completion_info($course);
373
                    $completed = $completion->is_course_complete($userid);
374
                    $completionhascriteria = $completion->has_criteria();
375
                    $completionusertracked = $completion->is_tracked_user($userid);
376
                    $progress = \core_completion\progress::get_course_progress_percentage($course, $userid);
377
                }
378
            }
379
 
380
            $lastaccess = null;
381
            // Check if last access is a hidden field.
382
            $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
383
            $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']);
384
            if (!$canviewlastaccess) {
385
                $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context);
386
            }
387
 
388
            if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) {
389
                $lastaccess = $user->lastcourseaccess[$course->id];
390
            }
391
 
392
            $hidden = false;
393
            if ($sameuser) {
394
                $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0));
395
            }
396
 
397
            // Retrieve course overview used files.
398
            $courselist = new core_course_list_element($course);
399
            $overviewfiles = array();
400
            foreach ($courselist->get_course_overviewfiles() as $file) {
401
                $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
402
                                                                        $file->get_filearea(), null, $file->get_filepath(),
403
                                                                        $file->get_filename())->out(false);
404
                $overviewfiles[] = array(
405
                    'filename' => $file->get_filename(),
406
                    'fileurl' => $fileurl,
407
                    'filesize' => $file->get_filesize(),
408
                    'filepath' => $file->get_filepath(),
409
                    'mimetype' => $file->get_mimetype(),
410
                    'timemodified' => $file->get_timemodified(),
411
                );
412
            }
413
 
414
            $courseimage = \core_course\external\course_summary_exporter::get_course_image($course);
415
            if (!$courseimage) {
416
                $courseimage = $OUTPUT->get_generated_url_for_course($context);
417
            }
418
 
419
            $courseresult = [
420
                'id' => $course->id,
421
                'shortname' => $course->shortname,
422
                'fullname' => $course->fullname,
423
                'displayname' => $displayname,
424
                'idnumber' => $course->idnumber,
425
                'visible' => $course->visible,
426
                'summary' => $course->summary,
427
                'summaryformat' => $course->summaryformat,
428
                'format' => $course->format,
429
                'courseimage' => $courseimage,
430
                'showgrades' => $course->showgrades,
431
                'lang' => clean_param($course->lang, PARAM_LANG),
432
                'enablecompletion' => $course->enablecompletion,
433
                'completionhascriteria' => $completionhascriteria,
434
                'completionusertracked' => $completionusertracked,
435
                'category' => $course->category,
436
                'progress' => $progress,
437
                'completed' => $completed,
438
                'startdate' => $course->startdate,
439
                'enddate' => $course->enddate,
440
                'marker' => $course->marker,
441
                'lastaccess' => $lastaccess,
442
                'isfavourite' => isset($favouritecourseids[$course->id]),
443
                'hidden' => $hidden,
444
                'overviewfiles' => $overviewfiles,
445
                'showactivitydates' => $course->showactivitydates,
446
                'showcompletionconditions' => $course->showcompletionconditions,
447
                'timemodified' => $course->timemodified,
448
            ];
449
            if ($returnusercount) {
450
                $courseresult['enrolledusercount'] = $enrolledusercount;
451
            }
452
            $result[] = $courseresult;
453
        }
454
 
455
        return $result;
456
    }
457
 
458
    /**
459
     * Returns description of method result value
460
     *
461
     * @return \core_external\external_description
462
     */
463
    public static function get_users_courses_returns() {
464
        return new external_multiple_structure(
465
            new external_single_structure(
466
                array(
467
                    'id'        => new external_value(PARAM_INT, 'id of course'),
468
                    'shortname' => new external_value(PARAM_RAW, 'short name of course'),
469
                    'fullname'  => new external_value(PARAM_RAW, 'long name of course'),
470
                    'displayname' => new external_value(PARAM_RAW, 'course display name for lists.', VALUE_OPTIONAL),
471
                    'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course',
472
                            VALUE_OPTIONAL),
473
                    'idnumber'  => new external_value(PARAM_RAW, 'id number of course'),
474
                    'visible'   => new external_value(PARAM_INT, '1 means visible, 0 means not yet visible course'),
475
                    'summary'   => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
476
                    'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
477
                    'format'    => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site', VALUE_OPTIONAL),
478
                    'courseimage' => new external_value(PARAM_URL, 'The course image URL', VALUE_OPTIONAL),
479
                    'showgrades' => new external_value(PARAM_BOOL, 'true if grades are shown, otherwise false', VALUE_OPTIONAL),
480
                    'lang'      => new external_value(PARAM_LANG, 'forced course language', VALUE_OPTIONAL),
481
                    'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false',
482
                                                                VALUE_OPTIONAL),
483
                    'completionhascriteria' => new external_value(PARAM_BOOL, 'If completion criteria is set.', VALUE_OPTIONAL),
484
                    'completionusertracked' => new external_value(PARAM_BOOL, 'If the user is completion tracked.', VALUE_OPTIONAL),
485
                    'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL),
486
                    'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL),
487
                    'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL),
488
                    'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
489
                    'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
490
                    'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL),
491
                    'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL),
492
                    'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this course a favourite.', VALUE_OPTIONAL),
493
                    'hidden' => new external_value(PARAM_BOOL, 'If the user hide the course from the dashboard.', VALUE_OPTIONAL),
494
                    'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL),
495
                    'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'),
496
                    'showcompletionconditions' => new external_value(PARAM_BOOL, 'Whether the activity completion conditions are shown or not'),
497
                    'timemodified' => new external_value(PARAM_INT, 'Last time course settings were updated (timestamp).',
498
                        VALUE_OPTIONAL),
499
                )
500
            )
501
        );
502
    }
503
 
504
    /**
505
     * Returns description of method parameters value
506
     *
507
     * @return \core_external\external_description
508
     */
509
    public static function get_potential_users_parameters() {
510
        return new external_function_parameters(
511
            array(
512
                'courseid' => new external_value(PARAM_INT, 'course id'),
513
                'enrolid' => new external_value(PARAM_INT, 'enrolment id'),
514
                'search' => new external_value(PARAM_RAW, 'query'),
515
                'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
516
                'page' => new external_value(PARAM_INT, 'Page number'),
517
                'perpage' => new external_value(PARAM_INT, 'Number per page'),
518
            )
519
        );
520
    }
521
 
522
    /**
523
     * Get potential users.
524
     *
525
     * @param int $courseid Course id
526
     * @param int $enrolid Enrolment id
527
     * @param string $search The query
528
     * @param boolean $searchanywhere Match anywhere in the string
529
     * @param int $page Page number
530
     * @param int $perpage Max per page
531
     * @return array An array of users
532
     */
533
    public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) {
534
        global $PAGE, $DB, $CFG;
535
 
536
        require_once($CFG->dirroot.'/enrol/locallib.php');
537
        require_once($CFG->dirroot.'/user/lib.php');
538
 
539
        $params = self::validate_parameters(
540
            self::get_potential_users_parameters(),
541
            array(
542
                'courseid' => $courseid,
543
                'enrolid' => $enrolid,
544
                'search' => $search,
545
                'searchanywhere' => $searchanywhere,
546
                'page' => $page,
547
                'perpage' => $perpage
548
            )
549
        );
550
        $context = context_course::instance($params['courseid']);
551
        try {
552
            self::validate_context($context);
553
        } catch (Exception $e) {
554
            $exceptionparam = new stdClass();
555
            $exceptionparam->message = $e->getMessage();
556
            $exceptionparam->courseid = $params['courseid'];
557
            throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
558
        }
559
        require_capability('moodle/course:enrolreview', $context);
560
 
561
        $course = $DB->get_record('course', array('id' => $params['courseid']));
562
        $manager = new course_enrolment_manager($PAGE, $course);
563
 
564
        $users = $manager->get_potential_users($params['enrolid'],
565
                                               $params['search'],
566
                                               $params['searchanywhere'],
567
                                               $params['page'],
568
                                               $params['perpage']);
569
 
570
        $results = array();
571
        // Add also extra user fields.
572
        $identityfields = \core_user\fields::get_identity_fields($context, true);
573
        $customprofilefields = [];
574
        foreach ($identityfields as $key => $value) {
575
            if ($fieldname = \core_user\fields::match_custom_field($value)) {
576
                unset($identityfields[$key]);
577
                $customprofilefields[$fieldname] = true;
578
            }
579
        }
580
        if ($customprofilefields) {
581
            $identityfields[] = 'customfields';
582
        }
583
        $requiredfields = array_merge(
584
            ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
585
            $identityfields
586
        );
587
        foreach ($users['users'] as $id => $user) {
588
            // Note: We pass the course here to validate that the current user can at least view user details in this course.
589
            // The user we are looking at is not in this course yet though - but we only fetch the minimal set of
590
            // user records, and the user has been validated to have course:enrolreview in this course. Otherwise
591
            // there is no way to find users who aren't in the course in order to enrol them.
592
            if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
593
                // For custom fields, only return the ones we actually need.
594
                if ($customprofilefields && array_key_exists('customfields', $userdetails)) {
595
                    foreach ($userdetails['customfields'] as $key => $data) {
596
                        if (!array_key_exists($data['shortname'], $customprofilefields)) {
597
                            unset($userdetails['customfields'][$key]);
598
                        }
599
                    }
600
                    $userdetails['customfields'] = array_values($userdetails['customfields']);
601
                }
602
                $results[] = $userdetails;
603
            }
604
        }
605
        return $results;
606
    }
607
 
608
    /**
609
     * Returns description of method result value
610
     *
611
     * @return \core_external\external_description
612
     */
613
    public static function get_potential_users_returns() {
614
        global $CFG;
615
        require_once($CFG->dirroot . '/user/externallib.php');
616
        return new external_multiple_structure(core_user_external::user_description());
617
    }
618
 
619
    /**
620
     * Returns description of method parameters
621
     *
622
     * @return external_function_parameters
623
     */
624
    public static function search_users_parameters(): external_function_parameters {
625
        return new external_function_parameters(
626
            [
627
                'courseid' => new external_value(PARAM_INT, 'course id'),
628
                'search' => new external_value(PARAM_RAW, 'query'),
629
                'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
630
                'page' => new external_value(PARAM_INT, 'Page number'),
631
                'perpage' => new external_value(PARAM_INT, 'Number per page'),
632
                'contextid' => new external_value(PARAM_INT, 'Context ID', VALUE_DEFAULT, null),
633
            ]
634
        );
635
    }
636
 
637
    /**
638
     * Search course participants.
639
     *
640
     * @param int $courseid Course id
641
     * @param string $search The query
642
     * @param bool $searchanywhere Match anywhere in the string
643
     * @param int $page Page number
644
     * @param int $perpage Max per page
645
     * @param ?int $contextid Context ID we are in - we might use search on activity level and its group mode can be different from course group mode.
646
     * @return array An array of users
647
     * @throws moodle_exception
648
     */
649
    public static function search_users(int $courseid, string $search, bool $searchanywhere, int $page, int $perpage, ?int $contextid = null): array {
650
        global $PAGE, $CFG;
651
 
652
        require_once($CFG->dirroot.'/enrol/locallib.php');
653
        require_once($CFG->dirroot.'/user/lib.php');
654
 
655
        $params = self::validate_parameters(
656
                self::search_users_parameters(),
657
                [
658
                    'courseid'       => $courseid,
659
                    'search'         => $search,
660
                    'searchanywhere' => $searchanywhere,
661
                    'page'           => $page,
662
                    'perpage'        => $perpage,
663
                    'contextid'      => $contextid,
664
                ],
665
        );
666
        if (isset($contextid)) {
667
            $context = context::instance_by_id($params['contextid']);
668
        } else {
669
            $context = context_course::instance($params['courseid']);
670
        }
671
        try {
672
            self::validate_context($context);
673
        } catch (Exception $e) {
674
            $exceptionparam = new stdClass();
675
            $exceptionparam->message = $e->getMessage();
676
            $exceptionparam->courseid = $params['courseid'];
677
            throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
678
        }
679
        course_require_view_participants($context);
680
 
681
        $course = get_course($params['courseid']);
682
        $manager = new course_enrolment_manager($PAGE, $course);
683
 
684
        $users = $manager->search_users(
685
            $params['search'],
686
            $params['searchanywhere'],
687
            $params['page'],
688
            $params['perpage'],
689
            false,
690
            $params['contextid']
691
        );
692
 
693
        $results = [];
694
        // Add also extra user fields.
695
        $requiredfields = array_merge(
696
                ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
697
                // TODO Does not support custom user profile fields (MDL-70456).
698
                \core_user\fields::get_identity_fields($context, false)
699
        );
700
        foreach ($users['users'] as $user) {
701
            if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
702
                $results[] = $userdetails;
703
            }
704
        }
705
        return $results;
706
    }
707
 
708
    /**
709
     * Returns description of method result value
710
     *
711
     * @return external_multiple_structure
712
     */
713
    public static function search_users_returns(): external_multiple_structure {
714
        global $CFG;
715
        require_once($CFG->dirroot . '/user/externallib.php');
716
        return new external_multiple_structure(core_user_external::user_description());
717
    }
718
 
719
    /**
720
     * Returns description of method parameters
721
     *
722
     * @return external_function_parameters
723
     */
724
    public static function get_enrolled_users_parameters() {
725
        return new external_function_parameters(
726
            [
727
                'courseid' => new external_value(PARAM_INT, 'course id'),
728
                'options'  => new external_multiple_structure(
729
                    new external_single_structure(
730
                        [
731
                            'name'  => new external_value(PARAM_ALPHANUMEXT, 'option name'),
732
                            'value' => new external_value(PARAM_RAW, 'option value')
733
                        ]
734
                    ), 'Option names:
735
                            * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
736
                            * groupid (integer) return only users in this group id. If the course has groups enabled and this param
737
                                                isn\'t defined, returns all the viewable users.
738
                                                This option requires \'moodle/site:accessallgroups\' on the course context if the
739
                                                user doesn\'t belong to the group.
740
                            * onlyactive (integer) return only users with active enrolments and matching time restrictions.
741
                                                This option requires \'moodle/course:enrolreview\' on the course context.
742
                                                Please note that this option can\'t
743
                                                be used together with onlysuspended (only one can be active).
744
                            * onlysuspended (integer) return only suspended users. This option requires
745
                                            \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
746
                                                be used together with onlyactive (only one can be active).
747
                            * userfields (\'string, string, ...\') return only the values of these user fields.
748
                            * limitfrom (integer) sql limit from.
749
                            * limitnumber (integer) maximum number of returned users.
750
                            * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
751
                            * sortdirection (string) ASC or DESC',
752
                            VALUE_DEFAULT, []),
753
            ]
754
        );
755
    }
756
 
757
    /**
758
     * Get course participants details
759
     *
760
     * @param int $courseid  course id
761
     * @param array $options options {
762
     *                                'name' => option name
763
     *                                'value' => option value
764
     *                               }
765
     * @return array An array of users
766
     */
767
    public static function get_enrolled_users($courseid, $options = []) {
768
        global $CFG, $USER, $DB;
769
 
770
        require_once($CFG->dirroot . '/course/lib.php');
771
        require_once($CFG->dirroot . "/user/lib.php");
772
 
773
        $params = self::validate_parameters(
774
            self::get_enrolled_users_parameters(),
775
            [
776
                'courseid'=>$courseid,
777
                'options'=>$options
778
            ]
779
        );
780
        $withcapability = '';
781
        $groupid        = 0;
782
        $onlyactive     = false;
783
        $onlysuspended  = false;
784
        $userfields     = [];
785
        $limitfrom = 0;
786
        $limitnumber = 0;
787
        $sortby = 'us.id';
788
        $sortparams = [];
789
        $sortdirection = 'ASC';
790
        foreach ($options as $option) {
791
            switch ($option['name']) {
792
                case 'withcapability':
793
                    $withcapability = $option['value'];
794
                    break;
795
                case 'groupid':
796
                    $groupid = (int)$option['value'];
797
                    break;
798
                case 'onlyactive':
799
                    $onlyactive = !empty($option['value']);
800
                    break;
801
                case 'onlysuspended':
802
                    $onlysuspended = !empty($option['value']);
803
                    break;
804
                case 'userfields':
805
                    $thefields = explode(',', $option['value']);
806
                    foreach ($thefields as $f) {
807
                        $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
808
                    }
809
                    break;
810
                case 'limitfrom' :
811
                    $limitfrom = clean_param($option['value'], PARAM_INT);
812
                    break;
813
                case 'limitnumber' :
814
                    $limitnumber = clean_param($option['value'], PARAM_INT);
815
                    break;
816
                case 'sortby':
817
                    $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
818
                    if (!in_array($option['value'], $sortallowedvalues)) {
819
                        throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
820
                            $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
821
                    }
822
                    if ($option['value'] == 'siteorder') {
823
                        list($sortby, $sortparams) = users_order_by_sql('us');
824
                    } else {
825
                        $sortby = 'us.' . $option['value'];
826
                    }
827
                    break;
828
                case 'sortdirection':
829
                    $sortdirection = strtoupper($option['value']);
830
                    $directionallowedvalues = ['ASC', 'DESC'];
831
                    if (!in_array($sortdirection, $directionallowedvalues)) {
832
                        throw new invalid_parameter_exception('Invalid value for sortdirection parameter
833
                        (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
834
                    }
835
                    break;
836
            }
837
        }
838
 
839
        $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
840
        $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
841
        if ($courseid == SITEID) {
842
            $context = context_system::instance();
843
        } else {
844
            $context = $coursecontext;
845
        }
846
        try {
847
            self::validate_context($context);
848
        } catch (Exception $e) {
849
            $exceptionparam = new stdClass();
850
            $exceptionparam->message = $e->getMessage();
851
            $exceptionparam->courseid = $params['courseid'];
852
            throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
853
        }
854
 
855
        course_require_view_participants($context);
856
 
857
        // to overwrite this parameter, you need role:review capability
858
        if ($withcapability) {
859
            require_capability('moodle/role:review', $coursecontext);
860
        }
861
        // need accessallgroups capability if you want to overwrite this option
862
        if (!empty($groupid) && !groups_is_member($groupid)) {
863
            require_capability('moodle/site:accessallgroups', $coursecontext);
864
        }
865
        // to overwrite this option, you need course:enrolereview permission
866
        if ($onlyactive || $onlysuspended) {
867
            require_capability('moodle/course:enrolreview', $coursecontext);
868
        }
869
 
870
        list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
871
        $onlysuspended);
872
        $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
873
        $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
874
        $enrolledparams['contextlevel'] = CONTEXT_USER;
875
 
876
        $groupjoin = '';
877
        if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
878
                !has_capability('moodle/site:accessallgroups', $coursecontext)) {
879
            // Filter by groups the user can view.
880
            $usergroups = groups_get_user_groups($course->id);
881
            if (!empty($usergroups['0'])) {
882
                list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
883
                $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
884
                $enrolledparams = array_merge($enrolledparams, $groupparams);
885
            } else {
886
                // User doesn't belong to any group, so he can't see any user. Return an empty array.
887
                return [];
888
            }
889
        }
890
        $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
891
                  FROM {user} us
892
                  JOIN (
893
                      SELECT DISTINCT u.id $ctxselect
894
                        FROM {user} u $ctxjoin $groupjoin
895
                       WHERE u.id IN ($enrolledsql)
896
                  ) q ON q.id = us.id
897
             LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid)
898
                ORDER BY $sortby $sortdirection";
899
        $enrolledparams = array_merge($enrolledparams, $sortparams);
900
        $enrolledparams['courseid'] = $courseid;
901
 
902
        $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
903
        $users = [];
904
        foreach ($enrolledusers as $user) {
905
            context_helper::preload_from_record($user);
906
            if ($userdetails = user_get_user_details($user, $course, $userfields)) {
907
                $users[] = $userdetails;
908
            }
909
        }
910
        $enrolledusers->close();
911
 
912
        return $users;
913
    }
914
 
915
    /**
916
     * Returns description of method result value
917
     *
918
     * @return \core_external\external_description
919
     */
920
    public static function get_enrolled_users_returns() {
921
        return new external_multiple_structure(
922
            new external_single_structure(
923
                [
924
                    'id'    => new external_value(PARAM_INT, 'ID of the user'),
925
                    'username'    => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
926
                    'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
927
                    'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
928
                    'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
929
                    'email'       => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
930
                    'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
931
                    'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
932
                    'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
933
                    'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
934
                    'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
935
                    'idnumber'    => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
936
                    'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
937
                    'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
938
                    'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
939
                    'lastcourseaccess'  => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL),
940
                    'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
941
                    'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
942
                    'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
943
                    'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
944
                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
945
                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
946
                    'customfields' => new external_multiple_structure(
947
                        new external_single_structure(
948
                            [
949
                                'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
950
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
951
                                'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
952
                                'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
953
                            ]
954
                        ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
955
                    'groups' => new external_multiple_structure(
956
                        new external_single_structure(
957
                            [
958
                                'id'  => new external_value(PARAM_INT, 'group id'),
959
                                'name' => new external_value(PARAM_RAW, 'group name'),
960
                                'description' => new external_value(PARAM_RAW, 'group description'),
961
                                'descriptionformat' => new external_format_value('description'),
962
                            ]
963
                        ), 'user groups', VALUE_OPTIONAL),
964
                    'roles' => new external_multiple_structure(
965
                        new external_single_structure(
966
                            [
967
                                'roleid'       => new external_value(PARAM_INT, 'role id'),
968
                                'name'         => new external_value(PARAM_RAW, 'role name'),
969
                                'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
970
                                'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
971
                            ]
972
                        ), 'user roles', VALUE_OPTIONAL),
973
                    'preferences' => new external_multiple_structure(
974
                        new external_single_structure(
975
                            [
976
                                'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
977
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
978
                            ]
979
                    ), 'User preferences', VALUE_OPTIONAL),
980
                    'enrolledcourses' => new external_multiple_structure(
981
                        new external_single_structure(
982
                            [
983
                                'id'  => new external_value(PARAM_INT, 'Id of the course'),
984
                                'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
985
                                'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
986
                            ]
987
                    ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
988
                ]
989
            )
990
        );
991
    }
992
 
993
    /**
994
     * Returns description of get_course_enrolment_methods() parameters
995
     *
996
     * @return external_function_parameters
997
     */
998
    public static function get_course_enrolment_methods_parameters() {
999
        return new external_function_parameters(
1000
            array(
1001
                'courseid' => new external_value(PARAM_INT, 'Course id')
1002
            )
1003
        );
1004
    }
1005
 
1006
    /**
1007
     * Get list of active course enrolment methods for current user.
1008
     *
1009
     * @param int $courseid
1010
     * @return array of course enrolment methods
1011
     * @throws moodle_exception
1012
     */
1013
    public static function get_course_enrolment_methods($courseid) {
1014
        global $DB;
1015
 
1016
        $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
1017
        self::validate_context(context_system::instance());
1018
 
1019
        $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
1020
        if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
1021
            throw new moodle_exception('coursehidden');
1022
        }
1023
 
1024
        $result = array();
1025
        $enrolinstances = enrol_get_instances($params['courseid'], true);
1026
        foreach ($enrolinstances as $enrolinstance) {
1027
            if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) {
1028
                if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) {
1029
                    $result[] = (array) $instanceinfo;
1030
                }
1031
            }
1032
        }
1033
        return $result;
1034
    }
1035
 
1036
    /**
1037
     * Returns description of get_course_enrolment_methods() result value
1038
     *
1039
     * @return \core_external\external_description
1040
     */
1041
    public static function get_course_enrolment_methods_returns() {
1042
        return new external_multiple_structure(
1043
            new external_single_structure(
1044
                array(
1045
                    'id' => new external_value(PARAM_INT, 'id of course enrolment instance'),
1046
                    'courseid' => new external_value(PARAM_INT, 'id of course'),
1047
                    'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'),
1048
                    'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'),
1049
                    'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'),
1050
                    'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL),
1051
                )
1052
            )
1053
        );
1054
    }
1055
 
1056
    /**
1057
     * Returns description of submit_user_enrolment_form parameters.
1058
     *
1059
     * @return external_function_parameters
1060
     */
1061
    public static function submit_user_enrolment_form_parameters() {
1062
        return new external_function_parameters([
1063
            'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
1064
        ]);
1065
    }
1066
 
1067
    /**
1068
     * External function that handles the user enrolment form submission.
1069
     *
1070
     * @param string $formdata The user enrolment form data in s URI encoded param string
1071
     * @return array An array consisting of the processing result and error flag, if available
1072
     */
1073
    public static function submit_user_enrolment_form($formdata) {
1074
        global $CFG, $DB, $PAGE;
1075
 
1076
        // Parameter validation.
1077
        $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]);
1078
 
1079
        $data = [];
1080
        parse_str($params['formdata'], $data);
1081
 
1082
        $userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST);
1083
        $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST);
1084
        $plugin = enrol_get_plugin($instance->enrol);
1085
        $course = get_course($instance->courseid);
1086
        $context = context_course::instance($course->id);
1087
        self::validate_context($context);
1088
 
1089
        require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1090
        $customformdata = [
1091
            'ue' => $userenrolment,
1092
            'modal' => true,
1093
            'enrolinstancename' => $plugin->get_instance_name($instance)
1094
        ];
1095
        $mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data);
1096
 
1097
        if ($validateddata = $mform->get_data()) {
1098
            if (!empty($validateddata->duration) && $validateddata->timeend == 0) {
1099
                $validateddata->timeend = $validateddata->timestart + $validateddata->duration;
1100
            }
1101
            require_once($CFG->dirroot . '/enrol/locallib.php');
1102
            $manager = new course_enrolment_manager($PAGE, $course);
1103
            $result = $manager->edit_enrolment($userenrolment, $validateddata);
1104
 
1105
            return ['result' => $result];
1106
        } else {
1107
            return ['result' => false, 'validationerror' => true];
1108
        }
1109
    }
1110
 
1111
    /**
1112
     * Returns description of submit_user_enrolment_form() result value
1113
     *
1114
     * @return \core_external\external_description
1115
     */
1116
    public static function submit_user_enrolment_form_returns() {
1117
        return new external_single_structure([
1118
            'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1119
            'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false),
1120
        ]);
1121
    }
1122
 
1123
    /**
1124
     * Returns description of unenrol_user_enrolment() parameters
1125
     *
1126
     * @return external_function_parameters
1127
     */
1128
    public static function unenrol_user_enrolment_parameters() {
1129
        return new external_function_parameters(
1130
            array(
1131
                'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
1132
            )
1133
        );
1134
    }
1135
 
1136
    /**
1137
     * External function that unenrols a given user enrolment.
1138
     *
1139
     * @param int $ueid The user enrolment ID.
1140
     * @return array An array consisting of the processing result, errors.
1141
     */
1142
    public static function unenrol_user_enrolment($ueid) {
1143
        global $CFG, $DB, $PAGE;
1144
 
1145
        $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
1146
            'ueid' => $ueid
1147
        ]);
1148
 
1149
        $result = false;
1150
        $errors = [];
1151
 
1152
        $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
1153
        if ($userenrolment) {
1154
            $userid = $userenrolment->userid;
1155
            $enrolid = $userenrolment->enrolid;
1156
            $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
1157
            $courseid = $enrol->courseid;
1158
            $course = get_course($courseid);
1159
            $context = context_course::instance($course->id);
1160
            self::validate_context($context);
1161
        } else {
1162
            $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
1163
        }
1164
 
1165
        // If the userenrolment exists, unenrol the user.
1166
        if (!isset($validationerrors)) {
1167
            require_once($CFG->dirroot . '/enrol/locallib.php');
1168
            $manager = new course_enrolment_manager($PAGE, $course);
1169
            $result = $manager->unenrol_user($userenrolment);
1170
        } else {
1171
            foreach ($validationerrors as $key => $errormessage) {
1172
                $errors[] = (object)[
1173
                    'key' => $key,
1174
                    'message' => $errormessage
1175
                ];
1176
            }
1177
        }
1178
 
1179
        return [
1180
            'result' => $result,
1181
            'errors' => $errors,
1182
        ];
1183
    }
1184
 
1185
    /**
1186
     * Returns description of unenrol_user_enrolment() result value
1187
     *
1188
     * @return \core_external\external_description
1189
     */
1190
    public static function unenrol_user_enrolment_returns() {
1191
        return new external_single_structure(
1192
            array(
1193
                'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
1194
                'errors' => new external_multiple_structure(
1195
                    new external_single_structure(
1196
                        array(
1197
                            'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
1198
                            'message' => new external_value(PARAM_TEXT, 'The error message'),
1199
                        )
1200
                    ), 'List of validation errors'
1201
                ),
1202
            )
1203
        );
1204
    }
1205
}
1206
 
1207
/**
1208
 * Role external functions
1209
 *
1210
 * @package    core_role
1211
 * @category   external
1212
 * @copyright  2011 Jerome Mouneyrac
1213
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1214
 * @since Moodle 2.2
1215
 */
1216
class core_role_external extends external_api {
1217
 
1218
    /**
1219
     * Returns description of method parameters
1220
     *
1221
     * @return external_function_parameters
1222
     */
1223
    public static function assign_roles_parameters() {
1224
        return new external_function_parameters(
1225
            array(
1226
                'assignments' => new external_multiple_structure(
1227
                    new external_single_structure(
1228
                        array(
1229
                            'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
1230
                            'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1231
                            'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL),
1232
                            'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in
1233
                                    (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1234
                            'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL),
1235
                        )
1236
                    )
1237
                )
1238
            )
1239
        );
1240
    }
1241
 
1242
    /**
1243
     * Manual role assignments to users
1244
     *
1245
     * @param array $assignments An array of manual role assignment
1246
     */
1247
    public static function assign_roles($assignments) {
1248
        global $DB;
1249
 
1250
        // Do basic automatic PARAM checks on incoming data, using params description
1251
        // If any problems are found then exceptions are thrown with helpful error messages
1252
        $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments));
1253
 
1254
        $transaction = $DB->start_delegated_transaction();
1255
 
1256
        foreach ($params['assignments'] as $assignment) {
1257
            // Ensure correct context level with a instance id or contextid is passed.
1258
            $context = self::get_context_from_params($assignment);
1259
 
1260
            // Ensure the current user is allowed to run this function in the enrolment context.
1261
            self::validate_context($context);
1262
            require_capability('moodle/role:assign', $context);
1263
 
1264
            // throw an exception if user is not able to assign the role in this context
1265
            $roles = get_assignable_roles($context, ROLENAME_SHORT);
1266
 
1267
            if (!array_key_exists($assignment['roleid'], $roles)) {
1268
                throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
1269
            }
1270
 
1271
            role_assign($assignment['roleid'], $assignment['userid'], $context->id);
1272
        }
1273
 
1274
        $transaction->allow_commit();
1275
    }
1276
 
1277
    /**
1278
     * Returns description of method result value
1279
     *
1280
     * @return null
1281
     */
1282
    public static function assign_roles_returns() {
1283
        return null;
1284
    }
1285
 
1286
 
1287
    /**
1288
     * Returns description of method parameters
1289
     *
1290
     * @return external_function_parameters
1291
     */
1292
    public static function unassign_roles_parameters() {
1293
        return new external_function_parameters(
1294
            array(
1295
                'unassignments' => new external_multiple_structure(
1296
                    new external_single_structure(
1297
                        array(
1298
                            'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
1299
                            'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
1300
                            'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL),
1301
                            'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in
1302
+                                    (block, course, coursecat, system, user, module)', VALUE_OPTIONAL),
1303
                            'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL),
1304
                        )
1305
                    )
1306
                )
1307
            )
1308
        );
1309
    }
1310
 
1311
     /**
1312
     * Unassign roles from users
1313
     *
1314
     * @param array $unassignments An array of unassignment
1315
     */
1316
    public static function unassign_roles($unassignments) {
1317
         global $DB;
1318
 
1319
        // Do basic automatic PARAM checks on incoming data, using params description
1320
        // If any problems are found then exceptions are thrown with helpful error messages
1321
        $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments));
1322
 
1323
        $transaction = $DB->start_delegated_transaction();
1324
 
1325
        foreach ($params['unassignments'] as $unassignment) {
1326
            // Ensure the current user is allowed to run this function in the unassignment context
1327
            $context = self::get_context_from_params($unassignment);
1328
            self::validate_context($context);
1329
            require_capability('moodle/role:assign', $context);
1330
 
1331
            // throw an exception if user is not able to unassign the role in this context
1332
            $roles = get_assignable_roles($context, ROLENAME_SHORT);
1333
            if (!array_key_exists($unassignment['roleid'], $roles)) {
1334
                throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
1335
            }
1336
 
1337
            role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id);
1338
        }
1339
 
1340
        $transaction->allow_commit();
1341
    }
1342
 
1343
   /**
1344
     * Returns description of method result value
1345
     *
1346
     * @return null
1347
     */
1348
    public static function unassign_roles_returns() {
1349
        return null;
1350
    }
1351
}