MDL-61804 admin: Add setting for course visibility sorting
[moodle.git] / enrol / tests / externallib_test.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/>.
17 defined('MOODLE_INTERNAL') || die();
19 global $CFG;
21 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
22 require_once($CFG->dirroot . '/enrol/externallib.php');
24 /**
25  * Enrol external PHPunit tests
26  *
27  * @package    core_enrol
28  * @category   external
29  * @copyright  2012 Jerome Mouneyrac
30  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31  * @since Moodle 2.4
32  */
33 class core_enrol_externallib_testcase extends externallib_advanced_testcase {
35     /**
36      * dataProvider for test_get_enrolled_users_visibility().
37      */
38     public function get_enrolled_users_visibility_provider() {
39         return array(
40             'Course without groups, default behavior (not filtering by cap, group, active)' =>
41             array(
42                 'settings' => array(
43                     'coursegroupmode' => NOGROUPS,
44                     'withcapability' => null,
45                     'groupid' => null,
46                     'onlyactive' => false,
47                     'allowedcaps' => array(),
48                 ),
49                 'results' => array( // Everybody can view everybody.
50                     'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
51                     'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
52                     'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
53                     'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
54                     'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
55                 ),
56             ),
58             'Course with visible groups, default behavior (not filtering by cap, group, active)' =>
59             array(
60                 'settings' => array(
61                     'coursegroupmode' => VISIBLEGROUPS,
62                     'withcapability' => null,
63                     'groupid' => null,
64                     'onlyactive' => false,
65                     'allowedcaps' => array(),
66                 ),
67                 'results' => array( // Everybody can view everybody.
68                     'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
69                     'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
70                     'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
71                     'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
72                     'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
73                 ),
74             ),
76             'Course with separate groups, default behavior (not filtering by cap, group, active)' =>
77             array(
78                 'settings' => array(
79                     'coursegroupmode' => SEPARATEGROUPS,
80                     'withcapability' => null,
81                     'groupid' => null,
82                     'onlyactive' => false,
83                     'allowedcaps' => array(),
84                 ),
85                 'results' => array( // Only users from own groups are visible.
86                     'user0' => array('canview' => array()), // Poor guy, cannot see anybody, himself included.
87                     'user1' => array('canview' => array('user1', 'userall')),
88                     'user2' => array('canview' => array('user2', 'user2su', 'userall')),
89                     'user31' => array('canview' => array('user31', 'user32', 'userall')),
90                     'userall' => array('canview' => array('user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
91                 ),
92             ),
94             'Course with separate groups, default behavior (not filtering but having moodle/site:accessallgroups)' =>
95             array(
96                 'settings' => array(
97                     'coursegroupmode' => VISIBLEGROUPS,
98                     'withcapability' => null,
99                     'groupid' => null,
100                     'onlyactive' => false,
101                     'allowedcaps' => array('moodle/site:accessallgroups'),
102                 ),
103                 'results' => array( // Everybody can view everybody.
104                     'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
105                     'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
106                     'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
107                     'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
108                     'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
109                 ),
110             ),
112             'Course with separate groups, filtering onlyactive (missing moodle/course:enrolreview)' =>
113             array(
114                 'settings' => array(
115                     'coursegroupmode' => SEPARATEGROUPS,
116                     'withcapability' => null,
117                     'groupid' => null,
118                     'onlyactive' => true,
119                     'allowedcaps' => array(),
120                 ),
121                 'results' => array( // returns exception, cannot view anybody without the cap.
122                     'user2' => array('exception' => array(
123                         'type' => 'required_capability_exception',
124                         'message' => 'Review course enrolments')),
125                     'userall' => array('exception' => array(
126                         'type' => 'required_capability_exception',
127                         'message' => 'Review course enrolments')),
128                 ),
129             ),
131             'Course with separate groups, filtering onlyactive (having moodle/course:enrolreview)' =>
132             array(
133                 'settings' => array(
134                     'coursegroupmode' => SEPARATEGROUPS,
135                     'withcapability' => null,
136                     'groupid' => null,
137                     'onlyactive' => true,
138                     'allowedcaps' => array('moodle/course:enrolreview'),
139                 ),
140                 'results' => array( // Suspended are not returned.
141                     'user2' => array('canview' => array('user2', 'userall')),
142                     'user31' => array('canview' => array('user31', 'user32', 'userall')),
143                     'userall' => array('canview' => array('user1', 'user2', 'user31', 'user32', 'userall')),
144                 ),
145             ),
147             'Course with separate groups, filtering by groupid (not having moodle/site:accessallgroups)' =>
148             array(
149                 'settings' => array(
150                     'coursegroupmode' => SEPARATEGROUPS,
151                     'withcapability' => null,
152                     'groupid' => 'group2',
153                     'onlyactive' => false,
154                     'allowedcaps' => array(),
155                 ),
156                 'results' => array( // Only group 2 members and only for members. Exception for non-members.
157                     'user0' => array('exception' => array(
158                         'type' => 'required_capability_exception',
159                         'message' => 'Access all groups')),
160                     'user1' => array('exception' => array(
161                         'type' => 'required_capability_exception',
162                         'message' => 'Access all groups')),
163                     'user2' => array('canview' => array('user2', 'user2su', 'userall')),
164                     'userall' => array('canview' => array('user2', 'user2su', 'userall')),
165                 ),
166             ),
168             'Course with separate groups, filtering by groupid (having moodle/site:accessallgroups)' =>
169             array(
170                 'settings' => array(
171                     'coursegroupmode' => SEPARATEGROUPS,
172                     'withcapability' => null,
173                     'groupid' => 'group2',
174                     'onlyactive' => false,
175                     'allowedcaps' => array('moodle/site:accessallgroups'),
176                 ),
177                 'results' => array( // All users with 'moodle/site:accessallgroups' can view group 2
178                     'user0' => array('canview' => array('user2', 'user2su', 'userall')),
179                     'user1' => array('canview' => array('user2', 'user2su', 'userall')),
180                     'user2' => array('canview' => array('user2', 'user2su', 'userall')),
181                     'userall' => array('canview' => array('user2', 'user2su', 'userall')),
182                 ),
183             ),
185             'Course with separate groups, filtering by withcapability (not having moodle/role:review)' =>
186             array(
187                 'settings' => array(
188                     'coursegroupmode' => SEPARATEGROUPS,
189                     'withcapability' => 'moodle/course:bulkmessaging',
190                     'groupid' => null,
191                     'onlyactive' => false,
192                     'allowedcaps' => array(),
193                 ),
194                 'results' => array( // No user has 'moodle/role:review' so exception.
195                     'user0' => array('exception' => array(
196                         'type' => 'required_capability_exception',
197                         'message' => 'Review permissions for others')),
198                     'user1' => array('exception' => array(
199                         'type' => 'required_capability_exception',
200                         'message' => 'Review permissions for others')),
201                     'user2' => array('exception' => array(
202                         'type' => 'required_capability_exception',
203                         'message' => 'Review permissions for others')),
204                     'userall' => array('exception' => array(
205                         'type' => 'required_capability_exception',
206                         'message' => 'Review permissions for others')),
207                 ),
208             ),
210             'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
211             array(
212                 'settings' => array(
213                     'coursegroupmode' => SEPARATEGROUPS,
214                     'withcapability' => 'moodle/course:bulkmessaging',
215                     'groupid' => null,
216                     'onlyactive' => false,
217                     'allowedcaps' => array('moodle/role:review'),
218                 ),
219                 'results' => array( // No user has withcapability, but all have 'moodle/role:review'. Empties.
220                     'user0' => array('canview' => array()),
221                     'user1' => array('canview' => array()),
222                     'user2' => array('canview' => array()),
223                     'userall' => array('canview' => array()),
224                 ),
225             ),
227             'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
228             array(
229                 'settings' => array(
230                     'coursegroupmode' => SEPARATEGROUPS,
231                     'withcapability' => 'moodle/course:bulkmessaging',
232                     'groupid' => null,
233                     'onlyactive' => false,
234                     'allowedcaps' => array('moodle/role:review', 'moodle/course:bulkmessaging'),
235                 ),
236                 'results' => array( // Users (previous) have withcapability, and all have 'moodle/role:review'.
237                     'user0' => array('canview' => array()),
238                     'user1' => array('canview' => array('user1')),
239                     'user2' => array('canview' => array('user2')),
240                     'userall' => array('canview' => array('user1', 'user2', 'userall')),
241                 ),
242             ),
243         );
244     }
246     /**
247      * Verify get_enrolled_users() returned users are the expected in every situation.
248      *
249      * @dataProvider get_enrolled_users_visibility_provider
250      */
251     public function test_get_enrolled_users_visibility($settings, $results) {
253         global $USER;
255         $this->resetAfterTest();
257         // Create the course and the users.
258         $course = $this->getDataGenerator()->create_course(array('groupmode' => $settings['coursegroupmode']));
259         $coursecontext = context_course::instance($course->id);
260         $user0 = $this->getDataGenerator()->create_user(array('username' => 'user0'));     // A user without group.
261         $user1 = $this->getDataGenerator()->create_user(array('username' => 'user1'));     // User for group 1.
262         $user2 = $this->getDataGenerator()->create_user(array('username' => 'user2'));     // Two users for group 2.
263         $user2su = $this->getDataGenerator()->create_user(array('username' => 'user2su')); // (one suspended).
264         $user31 = $this->getDataGenerator()->create_user(array('username' => 'user31'));   // Two users for group 3.
265         $user32 = $this->getDataGenerator()->create_user(array('username' => 'user32'));   // (both enabled).
266         $userall = $this->getDataGenerator()->create_user(array('username' => 'userall')); // A user in all groups.
268         // Create utility array of created users, to produce better assertion messages.
269         $createdusers = array();
270         foreach (array($user0, $user1, $user2, $user2su, $user31, $user32, $userall) as $createduser) {
271             $createdusers[$createduser->id] = $createduser->username;
272         }
274         // Enrol the users in the course.
275         $this->getDataGenerator()->enrol_user($user0->id, $course->id);
276         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
277         $this->getDataGenerator()->enrol_user($user2->id, $course->id);
278         $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
279         $this->getDataGenerator()->enrol_user($user31->id, $course->id);
280         $this->getDataGenerator()->enrol_user($user32->id, $course->id);
281         $this->getDataGenerator()->enrol_user($userall->id, $course->id);
283         // Create 3 groups.
284         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
285         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
286         $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
288         // Add the users to the groups.
289         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
290         $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id));
291         $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2su->id));
292         $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user31->id));
293         $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user32->id));
294         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $userall->id));
295         $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $userall->id));
296         $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $userall->id));
298         // Create a role to add the allowedcaps. Users will have this role assigned.
299         $roleid = $this->getDataGenerator()->create_role();
300         // Allow the specified capabilities.
301         if (!empty($settings['allowedcaps'])) {
302             foreach ($settings['allowedcaps'] as $capability) {
303                 assign_capability($capability, CAP_ALLOW, $roleid, $coursecontext);
304             }
305         }
307         // For each of the users, configure everything, perform the call, and assert results.
308         foreach ($results as $user => $expectations) {
309             // Convert canview expectations into a nice array of ids for easier handling.
310             $canview = array();
311             $exception = null;
312             // Analyse the expectations.
313             if (isset($expectations['canview'])) {
314                 foreach ($expectations['canview'] as $canviewuser) {
315                     $canview[] = $createdusers[${$canviewuser}->id];
316                 }
317             } else if (isset($expectations['exception'])) {
318                 $exception = $expectations['exception'];
319                 $this->expectException($exception['type']);
320                 $this->expectExceptionMessage($exception['message']);
321             } else {
322                 // Failed, only canview and exception are supported.
323                 $this->markTestIncomplete('Incomplete, only canview and exception are supported');
324             }
325             // Switch to the user and assign the role.
326             $this->setUser(${$user});
327             role_assign($roleid, $USER->id, $coursecontext);
329             // Convert groupid to proper id.
330             $groupid = 0;
331             if (isset($settings['groupid'])) {
332                 $groupid = ${$settings['groupid']}->id;
333             }
335             // Call to the function.
336             $options = array(
337                 array('name' => 'withcapability', 'value' => $settings['withcapability']),
338                 array('name' => 'groupid', 'value' => $groupid),
339                 array('name' => 'onlyactive', 'value' => $settings['onlyactive']),
340                 array('name' => 'userfields', 'value' => 'id')
341             );
342             $enrolledusers = core_enrol_external::get_enrolled_users($course->id, $options);
344             // We need to execute the return values cleaning process to simulate the web service server.
345             $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
347             // We are only interested in ids to check visibility.
348             $viewed = array();
349             // Verify the user canview the expected users.
350             foreach ($enrolledusers as $enrolleduser) {
351                 $viewed[] = $createdusers[$enrolleduser['id']];
352             }
353             // Verify viewed matches canview expectation (using canonicalize to ignore ordering).
354             $this->assertEquals($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'", 0, 1, true);
355         }
356     }
358     /**
359      * Test get_users_courses
360      */
361     public function test_get_users_courses() {
362         global $CFG, $DB;
363         require_once($CFG->dirroot . '/completion/criteria/completion_criteria_self.php');
365         $this->resetAfterTest(true);
366         $CFG->enablecompletion = 1;
368         $timenow = time();
369         $coursedata1 = array(
370             'fullname'         => '<b>Course 1</b>',                // Adding tags here to check that external_format_string works.
371             'shortname'         => '<b>Course 1</b>',               // Adding tags here to check that external_format_string works.
372             'summary'          => 'Lightwork Course 1 description',
373             'summaryformat'    => FORMAT_MOODLE,
374             'lang'             => 'en',
375             'enablecompletion' => true,
376             'showgrades'       => true,
377             'startdate'        => $timenow,
378             'enddate'          => $timenow + WEEKSECS,
379             'marker'           => 1
380         );
382         $coursedata2 = array(
383             'lang'             => 'kk', // Check invalid language pack.
384         );
386         $course1 = self::getDataGenerator()->create_course($coursedata1);
387         $course2 = self::getDataGenerator()->create_course($coursedata2);
388         $courses = array($course1, $course2);
389         $contexts = array ($course1->id => context_course::instance($course1->id),
390             $course2->id => context_course::instance($course2->id));
392         $student = $this->getDataGenerator()->create_user();
393         $otherstudent = $this->getDataGenerator()->create_user();
394         $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
395         $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentroleid);
396         $this->getDataGenerator()->enrol_user($otherstudent->id, $course1->id, $studentroleid);
397         $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentroleid);
399         // Force last access.
400         $timenow = time();
401         $lastaccess = array(
402             'userid' => $student->id,
403             'courseid' => $course1->id,
404             'timeaccess' => $timenow
405         );
406         $DB->insert_record('user_lastaccess', $lastaccess);
408         // Force completion, setting at least one criteria.
409         require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');
410         $criteriadata = new stdClass();
411         $criteriadata->id = $course1->id;
412         // Self completion.
413         $criteriadata->criteria_self = 1;
415         $criterion = new completion_criteria_self();
416         $criterion->update_config($criteriadata);
418         $ccompletion = new completion_completion(array('course' => $course1->id, 'userid' => $student->id));
419         $ccompletion->mark_complete();
421         // Set course hidden and favourited.
422         set_user_preference('block_myoverview_hidden_course_' . $course1->id, 1, $student);
423         $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($student->id));
424         $ufservice->create_favourite('core_course', 'courses', $course1->id, \context_system::instance());
426         $this->setUser($student);
427         // Call the external function.
428         $enrolledincourses = core_enrol_external::get_users_courses($student->id, true);
430         // We need to execute the return values cleaning process to simulate the web service server.
431         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
433         // Check we retrieve the good total number of enrolled users.
434         $this->assertEquals(2, count($enrolledincourses));
436         // We need to format summary and summaryformat before to compare them with those values returned by the webservice.
437         list($course1->summary, $course1->summaryformat) =
438              external_format_text($course1->summary, $course1->summaryformat, $contexts[$course1->id]->id, 'course', 'summary', 0);
440         // Check there are no differences between $course1 properties and course values returned by the webservice
441         // only for those fields listed in the $coursedata1 array.
442         $course1->fullname = external_format_string($course1->fullname, $contexts[$course1->id]->id);
443         $course1->shortname = external_format_string($course1->shortname, $contexts[$course1->id]->id);
444         foreach ($enrolledincourses as $courseenrol) {
445             if ($courseenrol['id'] == $course1->id) {
446                 foreach ($coursedata1 as $fieldname => $value) {
447                     $this->assertEquals($courseenrol[$fieldname], $course1->$fieldname);
448                 }
449                 // Text extra fields.
450                 $this->assertEquals($course1->fullname, $courseenrol['displayname']);
451                 $this->assertEquals([], $courseenrol['overviewfiles']);
452                 $this->assertEquals($timenow, $courseenrol['lastaccess']);
453                 $this->assertEquals(100.0, $courseenrol['progress']);
454                 $this->assertEquals(true, $courseenrol['completed']);
455                 $this->assertTrue($courseenrol['completionhascriteria']);
456                 $this->assertTrue($courseenrol['completionusertracked']);
457                 $this->assertTrue($courseenrol['hidden']);
458                 $this->assertTrue($courseenrol['isfavourite']);
459                 $this->assertEquals(2, $courseenrol['enrolledusercount']);
460             } else {
461                 // Check language pack. Should be empty since an incorrect one was used when creating the course.
462                 $this->assertEmpty($courseenrol['lang']);
463                 $this->assertEquals($course2->fullname, $courseenrol['displayname']);
464                 $this->assertEquals([], $courseenrol['overviewfiles']);
465                 $this->assertEquals(0, $courseenrol['lastaccess']);
466                 $this->assertEquals(0, $courseenrol['progress']);
467                 $this->assertEquals(false, $courseenrol['completed']);
468                 $this->assertFalse($courseenrol['completionhascriteria']);
469                 $this->assertFalse($courseenrol['completionusertracked']);
470                 $this->assertFalse($courseenrol['hidden']);
471                 $this->assertFalse($courseenrol['isfavourite']);
472                 $this->assertEquals(1, $courseenrol['enrolledusercount']);
473             }
474         }
476         // Check that returnusercount works correctly.
477         $enrolledincourses = core_enrol_external::get_users_courses($student->id, false);
478         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
479         foreach ($enrolledincourses as $courseenrol) {
480             $this->assertFalse(isset($courseenrol['enrolledusercount']));
481         }
483         // Now check that admin users can see all the info.
484         $this->setAdminUser();
486         $enrolledincourses = core_enrol_external::get_users_courses($student->id, true);
487         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
488         $this->assertEquals(2, count($enrolledincourses));
489         foreach ($enrolledincourses as $courseenrol) {
490             if ($courseenrol['id'] == $course1->id) {
491                 $this->assertEquals($timenow, $courseenrol['lastaccess']);
492                 $this->assertEquals(100.0, $courseenrol['progress']);
493                 $this->assertTrue($courseenrol['completionhascriteria']);
494                 $this->assertTrue($courseenrol['completionusertracked']);
495                 $this->assertFalse($courseenrol['isfavourite']);    // This always false.
496                 $this->assertFalse($courseenrol['hidden']); // This always false.
497             } else {
498                 $this->assertEquals(0, $courseenrol['progress']);
499                 $this->assertFalse($courseenrol['completionhascriteria']);
500                 $this->assertFalse($courseenrol['completionusertracked']);
501                 $this->assertFalse($courseenrol['isfavourite']);    // This always false.
502                 $this->assertFalse($courseenrol['hidden']); // This always false.
503             }
504         }
506         // Check other users can't see private info.
507         $this->setUser($otherstudent);
509         $enrolledincourses = core_enrol_external::get_users_courses($student->id, true);
510         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
511         $this->assertEquals(1, count($enrolledincourses));
513         $this->assertEquals($timenow, $enrolledincourses[0]['lastaccess']); // I can see this, not hidden.
514         $this->assertEquals(null, $enrolledincourses[0]['progress']);   // I can't see this, private.
516         // Change some global profile visibility fields.
517         $CFG->hiddenuserfields = 'lastaccess';
518         $enrolledincourses = core_enrol_external::get_users_courses($student->id, true);
519         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
521         $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting.
522     }
524     /**
525      * Test get_course_enrolment_methods
526      */
527     public function test_get_course_enrolment_methods() {
528         global $DB;
530         $this->resetAfterTest(true);
532         // Get enrolment plugins.
533         $selfplugin = enrol_get_plugin('self');
534         $this->assertNotEmpty($selfplugin);
535         $manualplugin = enrol_get_plugin('manual');
536         $this->assertNotEmpty($manualplugin);
538         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
539         $this->assertNotEmpty($studentrole);
541         $course1 = self::getDataGenerator()->create_course();
542         $coursedata = new stdClass();
543         $coursedata->visible = 0;
544         $course2 = self::getDataGenerator()->create_course($coursedata);
546         // Add enrolment methods for course.
547         $instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
548                                                                 'name' => 'Test instance 1',
549                                                                 'customint6' => 1,
550                                                                 'roleid' => $studentrole->id));
551         $instanceid2 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_DISABLED,
552                                                                 'name' => 'Test instance 2',
553                                                                 'roleid' => $studentrole->id));
555         $instanceid3 = $manualplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
556                                                                 'name' => 'Test instance 3'));
558         $enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id, 'status' => ENROL_INSTANCE_ENABLED));
559         $this->assertCount(2, $enrolmentmethods);
561         $this->setAdminUser();
563         // Check if information is returned.
564         $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course1->id);
565         $enrolmentmethods = external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(),
566                                                             $enrolmentmethods);
567         // Enrolment information is currently returned by self enrolment plugin, so count == 1.
568         // This should be changed as we implement get_enrol_info() for other enrolment plugins.
569         $this->assertCount(1, $enrolmentmethods);
571         $enrolmentmethod = $enrolmentmethods[0];
572         $this->assertEquals($course1->id, $enrolmentmethod['courseid']);
573         $this->assertEquals('self', $enrolmentmethod['type']);
574         $this->assertTrue($enrolmentmethod['status']);
575         $this->assertFalse(isset($enrolmentmethod['wsfunction']));
577         $instanceid4 = $selfplugin->add_instance($course2, array('status' => ENROL_INSTANCE_ENABLED,
578                                                                 'name' => 'Test instance 4',
579                                                                 'roleid' => $studentrole->id,
580                                                                 'customint6' => 1,
581                                                                 'password' => 'test'));
582         $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course2->id);
583         $enrolmentmethods = external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(),
584                                                             $enrolmentmethods);
585         $this->assertCount(1, $enrolmentmethods);
587         $enrolmentmethod = $enrolmentmethods[0];
588         $this->assertEquals($course2->id, $enrolmentmethod['courseid']);
589         $this->assertEquals('self', $enrolmentmethod['type']);
590         $this->assertTrue($enrolmentmethod['status']);
591         $this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']);
593         // Try to retrieve information using a normal user for a hidden course.
594         $user = self::getDataGenerator()->create_user();
595         $this->setUser($user);
596         try {
597             core_enrol_external::get_course_enrolment_methods($course2->id);
598         } catch (moodle_exception $e) {
599             $this->assertEquals('coursehidden', $e->errorcode);
600         }
601     }
603     public function get_enrolled_users_setup($capability) {
604         global $USER;
606         $this->resetAfterTest(true);
608         $return = new stdClass();
610         $return->course = self::getDataGenerator()->create_course();
611         $return->user1 = self::getDataGenerator()->create_user();
612         $return->user2 = self::getDataGenerator()->create_user();
613         $return->user3 = self::getDataGenerator()->create_user();
614         $this->setUser($return->user3);
616         // Set the required capabilities by the external function.
617         $return->context = context_course::instance($return->course->id);
618         $return->roleid = $this->assignUserCapability($capability, $return->context->id);
619         $this->assignUserCapability('moodle/user:viewdetails', $return->context->id, $return->roleid);
621         // Enrol the users in the course.
622         $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');
623         $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');
624         $this->getDataGenerator()->enrol_user($return->user3->id, $return->course->id, $return->roleid, 'manual');
626         return $return;
627     }
629     /**
630      * Test get_enrolled_users from core_enrol_external without additional
631      * parameters.
632      */
633     public function test_get_enrolled_users_without_parameters() {
634         $capability = 'moodle/course:viewparticipants';
635         $data = $this->get_enrolled_users_setup($capability);
637         // Call the external function.
638         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
640         // We need to execute the return values cleaning process to simulate the web service server.
641         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
643         // Check the result set.
644         $this->assertEquals(3, count($enrolledusers));
645         $this->assertArrayHasKey('email', $enrolledusers[0]);
646     }
648     /**
649      * Test get_enrolled_users from core_enrol_external with some parameters set.
650      */
651     public function test_get_enrolled_users_with_parameters() {
652         $capability = 'moodle/course:viewparticipants';
653         $data = $this->get_enrolled_users_setup($capability);
655         // Call the function with some parameters set.
656         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id, array(
657             array('name' => 'limitfrom', 'value' => 2),
658             array('name' => 'limitnumber', 'value' => 1),
659             array('name' => 'userfields', 'value' => 'id')
660         ));
662         // We need to execute the return values cleaning process to simulate the web service server.
663         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
665         // Check the result set, we should only get the 3rd result, which is $user3.
666         $this->assertCount(1, $enrolledusers);
667         $this->assertEquals($data->user3->id, $enrolledusers[0]['id']);
668         $this->assertArrayHasKey('id', $enrolledusers[0]);
669         $this->assertArrayNotHasKey('email', $enrolledusers[0]);
670     }
673     /**
674      * Test get_enrolled_users last course access.
675      */
676     public function test_get_enrolled_users_including_lastcourseaccess() {
677         global $DB;
678         $capability = 'moodle/course:viewparticipants';
679         $data = $this->get_enrolled_users_setup($capability);
681         // Call the external function.
682         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
683         // We need to execute the return values cleaning process to simulate the web service server.
684         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
686         // Check the result set.
687         $this->assertEquals(3, count($enrolledusers));
688         $this->assertArrayHasKey('email', $enrolledusers[0]);
689         $this->assertEquals(0, $enrolledusers[0]['lastcourseaccess']);
690         $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
691         $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']);   // We forced an access to the course via setUser.
693         // Force last access.
694         $timenow = time();
695         $lastaccess = array(
696             'userid' => $enrolledusers[0]['id'],
697             'courseid' => $data->course->id,
698             'timeaccess' => $timenow
699         );
700         $DB->insert_record('user_lastaccess', $lastaccess);
702         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
703         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
705         // Check the result set.
706         $this->assertEquals(3, count($enrolledusers));
707         $this->assertEquals($timenow, $enrolledusers[0]['lastcourseaccess']);
708         $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
709         $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']);
710     }
712     /**
713      * Test get_enrolled_users from core_enrol_external with capability to
714      * viewparticipants removed.
715      *
716      * @expectedException moodle_exception
717      */
718     public function test_get_enrolled_users_without_capability() {
719         $capability = 'moodle/course:viewparticipants';
720         $data = $this->get_enrolled_users_setup($capability);
722         // Call without required capability.
723         $this->unassignUserCapability($capability, $data->context->id, $data->roleid);
724         $categories = core_enrol_external::get_enrolled_users($data->course->id);
725     }
727     public function get_enrolled_users_with_capability_setup($capability) {
728         global $USER, $DB;
730         $this->resetAfterTest(true);
732         $return = new stdClass();
734         // Create the course and fetch its context.
735         $return->course = self::getDataGenerator()->create_course();
736         $context = context_course::instance($return->course->id);
738         // Create one teacher, and two students.
739         $return->teacher = self::getDataGenerator()->create_user();
740         $return->student1 = self::getDataGenerator()->create_user();
741         $return->student2 = self::getDataGenerator()->create_user();
743         // Create a new student role based on the student archetype but with the capability prohibitted.
744         $fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student');
745         assign_capability($capability, CAP_PROHIBIT, $fakestudentroleid, $context->id);
747         // Enrol all of the users in the course.
748         // * 'teacher'  is an editing teacher.
749         // * 'student1' is a standard student.
750         // * 'student2' is a student with the capability prohibitted.
751         $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
752         $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
753         $this->getDataGenerator()->enrol_user($return->teacher->id, $return->course->id, $editingteacherroleid);
754         $this->getDataGenerator()->enrol_user($return->student1->id, $return->course->id, $studentroleid);
755         $this->getDataGenerator()->enrol_user($return->student2->id, $return->course->id, $fakestudentroleid);
757         // Log in as the teacher.
758         $this->setUser($return->teacher);
760         // Clear caches.
761         accesslib_clear_all_caches_for_unit_testing();
763         return $return;
764     }
766     /**
767      * Test get_enrolled_users_with_capability without additional paramaters.
768      */
769     public function test_get_enrolled_users_with_capability_without_parameters() {
770         $capability = 'moodle/course:viewparticipants';
771         $data = $this->get_enrolled_users_with_capability_setup($capability);
773         $result = core_enrol_external::get_enrolled_users_with_capability(
774             array(
775                 'coursecapabilities' => array(
776                     'courseid' => $data->course->id,
777                     'capabilities' => array(
778                         $capability,
779                     ),
780                 ),
781             ),
782             array()
783         );
785         // We need to execute the return values cleaning process to simulate the web service server.
786         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
788         // Check an array containing the expected user for the course capability is returned.
789         $expecteduserlist = $result[0];
790         $this->assertEquals($data->course->id, $expecteduserlist['courseid']);
791         $this->assertEquals($capability, $expecteduserlist['capability']);
792         $this->assertEquals(2, count($expecteduserlist['users']));
793     }
795     /**
796      * Test get_enrolled_users_with_capability
797      */
798     public function test_get_enrolled_users_with_capability_with_parameters () {
799         $capability = 'moodle/course:viewparticipants';
800         $data = $this->get_enrolled_users_with_capability_setup($capability);
802         $result = core_enrol_external::get_enrolled_users_with_capability(
803             array(
804                 'coursecapabilities' => array(
805                     'courseid' => $data->course->id,
806                     'capabilities' => array(
807                         $capability,
808                     ),
809                 ),
810             ),
811             array(
812                 array('name' => 'limitfrom', 'value' => 1),
813                 array('name' => 'limitnumber', 'value' => 1),
814                 array('name' => 'userfields', 'value' => 'id')
815             )
816         );
818         // We need to execute the return values cleaning process to simulate the web service server.
819         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
821         // Check an array containing the expected user for the course capability is returned.
822         $expecteduserlist = $result[0]['users'];
823         $expecteduser = reset($expecteduserlist);
824         $this->assertEquals(1, count($expecteduserlist));
825         $this->assertEquals($data->student1->id, $expecteduser['id']);
826     }
828     /**
829      * Test get_enrolled_users last course access.
830      */
831     public function test_get_enrolled_users_with_capability_including_lastcourseaccess() {
832         global $DB;
833         $capability = 'moodle/course:viewparticipants';
834         $data = $this->get_enrolled_users_with_capability_setup($capability);
836         $parameters = array(
837             'coursecapabilities' => array(
838                 'courseid' => $data->course->id,
839                 'capabilities' => array(
840                     $capability,
841                 ),
842             ),
843         );
845         $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array());
846         // We need to execute the return values cleaning process to simulate the web service server.
847         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
849         // Check an array containing the expected user for the course capability is returned.
850         $expecteduserlist = $result[0];
851         $this->assertEquals($data->course->id, $expecteduserlist['courseid']);
852         $this->assertEquals($capability, $expecteduserlist['capability']);
853         $this->assertEquals(2, count($expecteduserlist['users']));
854         // We forced an access to the course via setUser.
855         $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
856         $this->assertEquals(0, $expecteduserlist['users'][1]['lastcourseaccess']);
858         // Force last access.
859         $timenow = time();
860         $lastaccess = array(
861             'userid' => $expecteduserlist['users'][1]['id'],
862             'courseid' => $data->course->id,
863             'timeaccess' => $timenow
864         );
865         $DB->insert_record('user_lastaccess', $lastaccess);
867         $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array());
868         // We need to execute the return values cleaning process to simulate the web service server.
869         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
871         // Check the result set.
872         $expecteduserlist = $result[0];
873         $this->assertEquals(2, count($expecteduserlist['users']));
874         $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
875         $this->assertEquals($timenow, $expecteduserlist['users'][1]['lastcourseaccess']);
876     }
878     /**
879      * Test for core_enrol_external::edit_user_enrolment().
880      */
881     public function test_edit_user_enrolment() {
882         global $DB;
884         $this->resetAfterTest(true);
885         $datagen = $this->getDataGenerator();
887         /** @var enrol_manual_plugin $manualplugin */
888         $manualplugin = enrol_get_plugin('manual');
889         $this->assertNotNull($manualplugin);
891         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
892         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
893         $course = $datagen->create_course();
894         $user = $datagen->create_user();
895         $teacher = $datagen->create_user();
897         $instanceid = null;
898         $instances = enrol_get_instances($course->id, true);
899         foreach ($instances as $inst) {
900             if ($inst->enrol == 'manual') {
901                 $instanceid = (int)$inst->id;
902                 break;
903             }
904         }
905         if (empty($instanceid)) {
906             $instanceid = $manualplugin->add_default_instance($course);
907             if (empty($instanceid)) {
908                 $instanceid = $manualplugin->add_instance($course);
909             }
910         }
911         $this->assertNotNull($instanceid);
913         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
914         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
915         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
916         $ueid = (int)$DB->get_field(
917             'user_enrolments',
918             'id',
919             ['enrolid' => $instance->id, 'userid' => $user->id],
920             MUST_EXIST
921         );
923         // Login as teacher.
924         $this->setUser($teacher);
926         $now = new DateTime();
927         $nowtime = $now->getTimestamp();
929         // Invalid data.
930         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nowtime);
931         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
932         $this->assertFalse($data['result']);
933         $this->assertNotEmpty($data['errors']);
935         // Valid data.
936         $nextmonth = clone($now);
937         $nextmonth->add(new DateInterval('P1M'));
938         $nextmonthtime = $nextmonth->getTimestamp();
939         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nextmonthtime);
940         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
941         $this->assertTrue($data['result']);
942         $this->assertEmpty($data['errors']);
944         // Check updated user enrolment.
945         $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
946         $this->assertEquals(ENROL_USER_ACTIVE, $ue->status);
947         $this->assertEquals($nowtime, $ue->timestart);
948         $this->assertEquals($nextmonthtime, $ue->timeend);
950         // Suspend user.
951         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_SUSPENDED);
952         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
953         $this->assertTrue($data['result']);
954         $this->assertEmpty($data['errors']);
956         // Check updated user enrolment.
957         $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
958         $this->assertEquals(ENROL_USER_SUSPENDED, $ue->status);
959     }
961     /**
962      * dataProvider for test_submit_user_enrolment_form().
963      */
964     public function submit_user_enrolment_form_provider() {
965         $now = new DateTime();
967         $nextmonth = clone($now);
968         $nextmonth->add(new DateInterval('P1M'));
970         return [
971             'Invalid data' => [
972                 'customdata' => [
973                     'status' => ENROL_USER_ACTIVE,
974                     'timestart' => [
975                         'day' => $now->format('j'),
976                         'month' => $now->format('n'),
977                         'year' => $now->format('Y'),
978                         'hour' => $now->format('G'),
979                         'minute' => 0,
980                         'enabled' => 1,
981                     ],
982                     'timeend' => [
983                         'day' => $now->format('j'),
984                         'month' => $now->format('n'),
985                         'year' => $now->format('Y'),
986                         'hour' => $now->format('G'),
987                         'minute' => 0,
988                         'enabled' => 1,
989                     ],
990                 ],
991                 'expectedresult' => false,
992                 'validationerror' => true,
993             ],
994             'Valid data' => [
995                 'customdata' => [
996                     'status' => ENROL_USER_ACTIVE,
997                     'timestart' => [
998                         'day' => $now->format('j'),
999                         'month' => $now->format('n'),
1000                         'year' => $now->format('Y'),
1001                         'hour' => $now->format('G'),
1002                         'minute' => 0,
1003                         'enabled' => 1,
1004                     ],
1005                     'timeend' => [
1006                         'day' => $nextmonth->format('j'),
1007                         'month' => $nextmonth->format('n'),
1008                         'year' => $nextmonth->format('Y'),
1009                         'hour' => $nextmonth->format('G'),
1010                         'minute' => 0,
1011                         'enabled' => 1,
1012                     ],
1013                 ],
1014                 'expectedresult' => true,
1015                 'validationerror' => false
1016             ],
1017             'Suspend user' => [
1018                 'customdata' => [
1019                     'status' => ENROL_USER_SUSPENDED,
1020                 ],
1021                 'expectedresult' => true,
1022                 'validationerror' => false
1023             ],
1024         ];
1025     }
1027     /**
1028      * @param array $customdata The data we are providing to the webservice.
1029      * @param bool $expectedresult The result we are expecting to receive from the webservice.
1030      * @param bool $validationerror The validationerror we are expecting to receive from the webservice.
1031      * @dataProvider submit_user_enrolment_form_provider
1032      */
1033     public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror) {
1034         global $CFG, $DB;
1036         $this->resetAfterTest(true);
1037         $datagen = $this->getDataGenerator();
1039         /** @var enrol_manual_plugin $manualplugin */
1040         $manualplugin = enrol_get_plugin('manual');
1042         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1043         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1044         $course = $datagen->create_course();
1045         $user = $datagen->create_user();
1046         $teacher = $datagen->create_user();
1048         $instanceid = null;
1049         $instances = enrol_get_instances($course->id, true);
1050         foreach ($instances as $inst) {
1051             if ($inst->enrol == 'manual') {
1052                 $instanceid = (int)$inst->id;
1053                 break;
1054             }
1055         }
1056         if (empty($instanceid)) {
1057             $instanceid = $manualplugin->add_default_instance($course);
1058             if (empty($instanceid)) {
1059                 $instanceid = $manualplugin->add_instance($course);
1060             }
1061         }
1062         $this->assertNotNull($instanceid);
1064         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1065         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1066         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1067         $ueid = (int) $DB->get_field(
1068                 'user_enrolments',
1069                 'id',
1070                 ['enrolid' => $instance->id, 'userid' => $user->id],
1071                 MUST_EXIST
1072         );
1074         // Login as teacher.
1075         $teacher->ignoresesskey = true;
1076         $this->setUser($teacher);
1078         $formdata = [
1079             'ue'        => $ueid,
1080             'ifilter'   => 0,
1081             'status'    => null,
1082             'timestart' => null,
1083             'duration'  => null,
1084             'timeend'   => null,
1085         ];
1087         $formdata = array_merge($formdata, $customdata);
1089         require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1090         $formdata = enrol_user_enrolment_form::mock_generate_submit_keys($formdata);
1092         $querystring = http_build_query($formdata, '', '&');
1094         $result = external_api::clean_returnvalue(
1095                 core_enrol_external::submit_user_enrolment_form_returns(),
1096                 core_enrol_external::submit_user_enrolment_form($querystring)
1097         );
1099         $this->assertEquals(
1100                 ['result' => $expectedresult, 'validationerror' => $validationerror],
1101                 $result,
1102                 '', 0.0, 10, true);
1104         if ($result['result']) {
1105             $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
1106             $this->assertEquals($formdata['status'], $ue->status);
1107         }
1108     }
1110     /**
1111      * Test for core_enrol_external::unenrol_user_enrolment().
1112      */
1113     public function test_unenerol_user_enrolment() {
1114         global $DB;
1116         $this->resetAfterTest(true);
1117         $datagen = $this->getDataGenerator();
1119         /** @var enrol_manual_plugin $manualplugin */
1120         $manualplugin = enrol_get_plugin('manual');
1121         $this->assertNotNull($manualplugin);
1123         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1124         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1125         $course = $datagen->create_course();
1126         $user = $datagen->create_user();
1127         $teacher = $datagen->create_user();
1129         $instanceid = null;
1130         $instances = enrol_get_instances($course->id, true);
1131         foreach ($instances as $inst) {
1132             if ($inst->enrol == 'manual') {
1133                 $instanceid = (int)$inst->id;
1134                 break;
1135             }
1136         }
1137         if (empty($instanceid)) {
1138             $instanceid = $manualplugin->add_default_instance($course);
1139             if (empty($instanceid)) {
1140                 $instanceid = $manualplugin->add_instance($course);
1141             }
1142         }
1143         $this->assertNotNull($instanceid);
1145         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1146         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1147         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1148         $ueid = (int)$DB->get_field(
1149             'user_enrolments',
1150             'id',
1151             ['enrolid' => $instance->id, 'userid' => $user->id],
1152             MUST_EXIST
1153         );
1155         // Login as teacher.
1156         $this->setUser($teacher);
1158         // Invalid data by passing invalid ueid.
1159         $data = core_enrol_external::unenrol_user_enrolment(101010);
1160         $data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
1161         $this->assertFalse($data['result']);
1162         $this->assertNotEmpty($data['errors']);
1164         // Valid data.
1165         $data = core_enrol_external::unenrol_user_enrolment($ueid);
1166         $data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
1167         $this->assertTrue($data['result']);
1168         $this->assertEmpty($data['errors']);
1170         // Check unenrol user enrolment.
1171         $ue = $DB->count_records('user_enrolments', ['id' => $ueid]);
1172         $this->assertEquals(0, $ue);
1173     }
1175     /**
1176      * Test for core_enrol_external::test_search_users().
1177      */
1178     public function test_search_users() {
1179         global $DB;
1181         $this->resetAfterTest(true);
1182         $datagen = $this->getDataGenerator();
1184         /** @var enrol_manual_plugin $manualplugin */
1185         $manualplugin = enrol_get_plugin('manual');
1186         $this->assertNotNull($manualplugin);
1188         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1189         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1191         $course1 = $datagen->create_course();
1192         $course2 = $datagen->create_course();
1194         $user1 = $datagen->create_user(['firstname' => 'user 1']);
1195         $user2 = $datagen->create_user(['firstname' => 'user 2']);
1196         $user3 = $datagen->create_user(['firstname' => 'user 3']);
1197         $teacher = $datagen->create_user(['firstname' => 'user 4']);
1199         $instanceid = null;
1200         $instances = enrol_get_instances($course1->id, true);
1201         foreach ($instances as $inst) {
1202             if ($inst->enrol == 'manual') {
1203                 $instanceid = (int)$inst->id;
1204                 break;
1205             }
1206         }
1207         if (empty($instanceid)) {
1208             $instanceid = $manualplugin->add_default_instance($course1);
1209             if (empty($instanceid)) {
1210                 $instanceid = $manualplugin->add_instance($course1);
1211             }
1212         }
1213         $this->assertNotNull($instanceid);
1215         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1216         $manualplugin->enrol_user($instance, $user1->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1217         $manualplugin->enrol_user($instance, $user2->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1218         $manualplugin->enrol_user($instance, $user3->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1219         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1221         $this->setUser($teacher);
1223         // Search for users in a course with enrolled users.
1224         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
1225         $this->assertCount(4, $result);
1227         $this->expectException('moodle_exception');
1228         // Search for users in a course without any enrolled users, shouldn't return anything.
1229         $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
1230         $this->assertCount(0, $result);
1232         // Search for invalid first name.
1233         $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
1234         $this->assertCount(0, $result);
1236         // Test pagination, it should return only 3 users.
1237         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 3);
1238         $this->assertCount(3, $result);
1240         // Test pagination, it should return only 3 users.
1241         $result = core_enrol_external::search_users($course1->id, 'user 1', true, 0, 1);
1242         $result = $result[0];
1243         $this->assertEquals($user1->id, $result['id']);
1244         $this->assertEquals($user1->email, $result['email']);
1245         $this->assertEquals(fullname($user1), $result['fullname']);
1247         $this->setUser($user1);
1249         // Search for users in a course with enrolled users.
1250         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
1251         $this->assertCount(4, $result);
1253         $this->expectException('moodle_exception');
1254         // Search for users in a course without any enrolled users, shouldn't return anything.
1255         $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
1256         $this->assertCount(0, $result);
1258         // Search for invalid first name.
1259         $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
1260         $this->assertCount(0, $result);
1261     }