Merge branch 'MDL-69145-master-3' of git://github.com/junpataleta/moodle
[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->assertEqualsCanonicalizing($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'");
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_users_courses with mathjax in the name.
526      */
527     public function test_get_users_courses_with_mathjax() {
528         global $DB;
530         $this->resetAfterTest(true);
532         // Enable MathJax filter in content and headings.
533         $this->configure_filters([
534             ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true],
535         ]);
537         // Create a course with MathJax in the name and summary.
538         $coursedata = [
539             'fullname'         => 'Course 1 $$(a+b)=2$$',
540             'shortname'         => 'Course 1 $$(a+b)=2$$',
541             'summary'          => 'Lightwork Course 1 description $$(a+b)=2$$',
542             'summaryformat'    => FORMAT_HTML,
543         ];
545         $course = self::getDataGenerator()->create_course($coursedata);
546         $context = context_course::instance($course->id);
548         // Enrol a student in the course.
549         $student = $this->getDataGenerator()->create_user();
550         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
551         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentroleid);
553         $this->setUser($student);
555         // Call the external function.
556         $enrolledincourses = core_enrol_external::get_users_courses($student->id, true);
558         // We need to execute the return values cleaning process to simulate the web service server.
559         $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses);
561         // Check that the amount of courses is the right one.
562         $this->assertCount(1, $enrolledincourses);
564         // Filter the values to compare them with the returned ones.
565         $course->fullname = external_format_string($course->fullname, $context->id);
566         $course->shortname = external_format_string($course->shortname, $context->id);
567         list($course->summary, $course->summaryformat) =
568              external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
570         // Compare the values.
571         $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['fullname']);
572         $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['shortname']);
573         $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['summary']);
574         $this->assertEquals($course->fullname, $enrolledincourses[0]['fullname']);
575         $this->assertEquals($course->shortname, $enrolledincourses[0]['shortname']);
576         $this->assertEquals($course->summary, $enrolledincourses[0]['summary']);
577     }
579     /**
580      * Test get_course_enrolment_methods
581      */
582     public function test_get_course_enrolment_methods() {
583         global $DB;
585         $this->resetAfterTest(true);
587         // Get enrolment plugins.
588         $selfplugin = enrol_get_plugin('self');
589         $this->assertNotEmpty($selfplugin);
590         $manualplugin = enrol_get_plugin('manual');
591         $this->assertNotEmpty($manualplugin);
593         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
594         $this->assertNotEmpty($studentrole);
596         $course1 = self::getDataGenerator()->create_course();
597         $coursedata = new stdClass();
598         $coursedata->visible = 0;
599         $course2 = self::getDataGenerator()->create_course($coursedata);
601         // Add enrolment methods for course.
602         $instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
603                                                                 'name' => 'Test instance 1',
604                                                                 'customint6' => 1,
605                                                                 'roleid' => $studentrole->id));
606         $instanceid2 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_DISABLED,
607                                                                 'name' => 'Test instance 2',
608                                                                 'roleid' => $studentrole->id));
610         $instanceid3 = $manualplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
611                                                                 'name' => 'Test instance 3'));
613         $enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id, 'status' => ENROL_INSTANCE_ENABLED));
614         $this->assertCount(2, $enrolmentmethods);
616         $this->setAdminUser();
618         // Check if information is returned.
619         $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course1->id);
620         $enrolmentmethods = external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(),
621                                                             $enrolmentmethods);
622         // Enrolment information is currently returned by self enrolment plugin, so count == 1.
623         // This should be changed as we implement get_enrol_info() for other enrolment plugins.
624         $this->assertCount(1, $enrolmentmethods);
626         $enrolmentmethod = $enrolmentmethods[0];
627         $this->assertEquals($course1->id, $enrolmentmethod['courseid']);
628         $this->assertEquals('self', $enrolmentmethod['type']);
629         $this->assertTrue($enrolmentmethod['status']);
630         $this->assertFalse(isset($enrolmentmethod['wsfunction']));
632         $instanceid4 = $selfplugin->add_instance($course2, array('status' => ENROL_INSTANCE_ENABLED,
633                                                                 'name' => 'Test instance 4',
634                                                                 'roleid' => $studentrole->id,
635                                                                 'customint6' => 1,
636                                                                 'password' => 'test'));
637         $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course2->id);
638         $enrolmentmethods = external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(),
639                                                             $enrolmentmethods);
640         $this->assertCount(1, $enrolmentmethods);
642         $enrolmentmethod = $enrolmentmethods[0];
643         $this->assertEquals($course2->id, $enrolmentmethod['courseid']);
644         $this->assertEquals('self', $enrolmentmethod['type']);
645         $this->assertTrue($enrolmentmethod['status']);
646         $this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']);
648         // Try to retrieve information using a normal user for a hidden course.
649         $user = self::getDataGenerator()->create_user();
650         $this->setUser($user);
651         try {
652             core_enrol_external::get_course_enrolment_methods($course2->id);
653         } catch (moodle_exception $e) {
654             $this->assertEquals('coursehidden', $e->errorcode);
655         }
656     }
658     public function get_enrolled_users_setup($capability) {
659         global $USER;
661         $this->resetAfterTest(true);
663         $return = new stdClass();
665         $return->course = self::getDataGenerator()->create_course();
666         $return->user1 = self::getDataGenerator()->create_user();
667         $return->user2 = self::getDataGenerator()->create_user();
668         $return->user3 = self::getDataGenerator()->create_user();
669         $this->setUser($return->user3);
671         // Set the required capabilities by the external function.
672         $return->context = context_course::instance($return->course->id);
673         $return->roleid = $this->assignUserCapability($capability, $return->context->id);
674         $this->assignUserCapability('moodle/user:viewdetails', $return->context->id, $return->roleid);
676         // Enrol the users in the course.
677         $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');
678         $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');
679         $this->getDataGenerator()->enrol_user($return->user3->id, $return->course->id, $return->roleid, 'manual');
681         return $return;
682     }
684     /**
685      * Test get_enrolled_users from core_enrol_external without additional
686      * parameters.
687      */
688     public function test_get_enrolled_users_without_parameters() {
689         $capability = 'moodle/course:viewparticipants';
690         $data = $this->get_enrolled_users_setup($capability);
692         // Call the external function.
693         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
695         // We need to execute the return values cleaning process to simulate the web service server.
696         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
698         // Check the result set.
699         $this->assertEquals(3, count($enrolledusers));
700         $this->assertArrayHasKey('email', $enrolledusers[0]);
701     }
703     /**
704      * Test get_enrolled_users from core_enrol_external with some parameters set.
705      */
706     public function test_get_enrolled_users_with_parameters() {
707         $capability = 'moodle/course:viewparticipants';
708         $data = $this->get_enrolled_users_setup($capability);
710         // Call the function with some parameters set.
711         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id, array(
712             array('name' => 'limitfrom', 'value' => 2),
713             array('name' => 'limitnumber', 'value' => 1),
714             array('name' => 'userfields', 'value' => 'id')
715         ));
717         // We need to execute the return values cleaning process to simulate the web service server.
718         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
720         // Check the result set, we should only get the 3rd result, which is $user3.
721         $this->assertCount(1, $enrolledusers);
722         $this->assertEquals($data->user3->id, $enrolledusers[0]['id']);
723         $this->assertArrayHasKey('id', $enrolledusers[0]);
724         $this->assertArrayNotHasKey('email', $enrolledusers[0]);
725     }
728     /**
729      * Test get_enrolled_users last course access.
730      */
731     public function test_get_enrolled_users_including_lastcourseaccess() {
732         global $DB;
733         $capability = 'moodle/course:viewparticipants';
734         $data = $this->get_enrolled_users_setup($capability);
736         // Call the external function.
737         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
738         // We need to execute the return values cleaning process to simulate the web service server.
739         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
741         // Check the result set.
742         $this->assertEquals(3, count($enrolledusers));
743         $this->assertArrayHasKey('email', $enrolledusers[0]);
744         $this->assertEquals(0, $enrolledusers[0]['lastcourseaccess']);
745         $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
746         $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']);   // We forced an access to the course via setUser.
748         // Force last access.
749         $timenow = time();
750         $lastaccess = array(
751             'userid' => $enrolledusers[0]['id'],
752             'courseid' => $data->course->id,
753             'timeaccess' => $timenow
754         );
755         $DB->insert_record('user_lastaccess', $lastaccess);
757         $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
758         $enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
760         // Check the result set.
761         $this->assertEquals(3, count($enrolledusers));
762         $this->assertEquals($timenow, $enrolledusers[0]['lastcourseaccess']);
763         $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
764         $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']);
765     }
767     /**
768      * Test get_enrolled_users from core_enrol_external with capability to
769      * viewparticipants removed.
770      */
771     public function test_get_enrolled_users_without_capability() {
772         $capability = 'moodle/course:viewparticipants';
773         $data = $this->get_enrolled_users_setup($capability);
775         // Call without required capability.
776         $this->unassignUserCapability($capability, $data->context->id, $data->roleid);
777         $this->expectException(moodle_exception::class);
778         $categories = core_enrol_external::get_enrolled_users($data->course->id);
779     }
781     public function get_enrolled_users_with_capability_setup($capability) {
782         global $USER, $DB;
784         $this->resetAfterTest(true);
786         $return = new stdClass();
788         // Create the course and fetch its context.
789         $return->course = self::getDataGenerator()->create_course();
790         $context = context_course::instance($return->course->id);
792         // Create one teacher, and two students.
793         $return->teacher = self::getDataGenerator()->create_user();
794         $return->student1 = self::getDataGenerator()->create_user();
795         $return->student2 = self::getDataGenerator()->create_user();
797         // Create a new student role based on the student archetype but with the capability prohibitted.
798         $fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student');
799         assign_capability($capability, CAP_PROHIBIT, $fakestudentroleid, $context->id);
801         // Enrol all of the users in the course.
802         // * 'teacher'  is an editing teacher.
803         // * 'student1' is a standard student.
804         // * 'student2' is a student with the capability prohibitted.
805         $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
806         $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
807         $this->getDataGenerator()->enrol_user($return->teacher->id, $return->course->id, $editingteacherroleid);
808         $this->getDataGenerator()->enrol_user($return->student1->id, $return->course->id, $studentroleid);
809         $this->getDataGenerator()->enrol_user($return->student2->id, $return->course->id, $fakestudentroleid);
811         // Log in as the teacher.
812         $this->setUser($return->teacher);
814         // Clear caches.
815         accesslib_clear_all_caches_for_unit_testing();
817         return $return;
818     }
820     /**
821      * Test get_enrolled_users_with_capability without additional paramaters.
822      */
823     public function test_get_enrolled_users_with_capability_without_parameters() {
824         $capability = 'moodle/course:viewparticipants';
825         $data = $this->get_enrolled_users_with_capability_setup($capability);
827         $result = core_enrol_external::get_enrolled_users_with_capability(
828             array(
829                 'coursecapabilities' => array(
830                     'courseid' => $data->course->id,
831                     'capabilities' => array(
832                         $capability,
833                     ),
834                 ),
835             ),
836             array()
837         );
839         // We need to execute the return values cleaning process to simulate the web service server.
840         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
842         // Check an array containing the expected user for the course capability is returned.
843         $expecteduserlist = $result[0];
844         $this->assertEquals($data->course->id, $expecteduserlist['courseid']);
845         $this->assertEquals($capability, $expecteduserlist['capability']);
846         $this->assertEquals(2, count($expecteduserlist['users']));
847     }
849     /**
850      * Test get_enrolled_users_with_capability
851      */
852     public function test_get_enrolled_users_with_capability_with_parameters () {
853         $capability = 'moodle/course:viewparticipants';
854         $data = $this->get_enrolled_users_with_capability_setup($capability);
856         $result = core_enrol_external::get_enrolled_users_with_capability(
857             array(
858                 'coursecapabilities' => array(
859                     'courseid' => $data->course->id,
860                     'capabilities' => array(
861                         $capability,
862                     ),
863                 ),
864             ),
865             array(
866                 array('name' => 'limitfrom', 'value' => 1),
867                 array('name' => 'limitnumber', 'value' => 1),
868                 array('name' => 'userfields', 'value' => 'id')
869             )
870         );
872         // We need to execute the return values cleaning process to simulate the web service server.
873         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
875         // Check an array containing the expected user for the course capability is returned.
876         $expecteduserlist = $result[0]['users'];
877         $expecteduser = reset($expecteduserlist);
878         $this->assertEquals(1, count($expecteduserlist));
879         $this->assertEquals($data->student1->id, $expecteduser['id']);
880     }
882     /**
883      * Test get_enrolled_users last course access.
884      */
885     public function test_get_enrolled_users_with_capability_including_lastcourseaccess() {
886         global $DB;
887         $capability = 'moodle/course:viewparticipants';
888         $data = $this->get_enrolled_users_with_capability_setup($capability);
890         $parameters = array(
891             'coursecapabilities' => array(
892                 'courseid' => $data->course->id,
893                 'capabilities' => array(
894                     $capability,
895                 ),
896             ),
897         );
899         $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array());
900         // We need to execute the return values cleaning process to simulate the web service server.
901         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
903         // Check an array containing the expected user for the course capability is returned.
904         $expecteduserlist = $result[0];
905         $this->assertEquals($data->course->id, $expecteduserlist['courseid']);
906         $this->assertEquals($capability, $expecteduserlist['capability']);
907         $this->assertEquals(2, count($expecteduserlist['users']));
908         // We forced an access to the course via setUser.
909         $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
910         $this->assertEquals(0, $expecteduserlist['users'][1]['lastcourseaccess']);
912         // Force last access.
913         $timenow = time();
914         $lastaccess = array(
915             'userid' => $expecteduserlist['users'][1]['id'],
916             'courseid' => $data->course->id,
917             'timeaccess' => $timenow
918         );
919         $DB->insert_record('user_lastaccess', $lastaccess);
921         $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array());
922         // We need to execute the return values cleaning process to simulate the web service server.
923         $result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
925         // Check the result set.
926         $expecteduserlist = $result[0];
927         $this->assertEquals(2, count($expecteduserlist['users']));
928         $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
929         $this->assertEquals($timenow, $expecteduserlist['users'][1]['lastcourseaccess']);
930     }
932     /**
933      * Test for core_enrol_external::edit_user_enrolment().
934      */
935     public function test_edit_user_enrolment() {
936         global $DB;
938         $this->resetAfterTest(true);
939         $datagen = $this->getDataGenerator();
941         /** @var enrol_manual_plugin $manualplugin */
942         $manualplugin = enrol_get_plugin('manual');
943         $this->assertNotNull($manualplugin);
945         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
946         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
947         $course = $datagen->create_course();
948         $user = $datagen->create_user();
949         $teacher = $datagen->create_user();
951         $instanceid = null;
952         $instances = enrol_get_instances($course->id, true);
953         foreach ($instances as $inst) {
954             if ($inst->enrol == 'manual') {
955                 $instanceid = (int)$inst->id;
956                 break;
957             }
958         }
959         if (empty($instanceid)) {
960             $instanceid = $manualplugin->add_default_instance($course);
961             if (empty($instanceid)) {
962                 $instanceid = $manualplugin->add_instance($course);
963             }
964         }
965         $this->assertNotNull($instanceid);
967         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
968         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
969         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
970         $ueid = (int)$DB->get_field(
971             'user_enrolments',
972             'id',
973             ['enrolid' => $instance->id, 'userid' => $user->id],
974             MUST_EXIST
975         );
977         // Login as teacher.
978         $this->setUser($teacher);
980         $now = new DateTime();
981         $nowtime = $now->getTimestamp();
983         // Invalid data.
984         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nowtime);
985         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
986         $this->assertFalse($data['result']);
987         $this->assertNotEmpty($data['errors']);
989         // Valid data.
990         $nextmonth = clone($now);
991         $nextmonth->add(new DateInterval('P1M'));
992         $nextmonthtime = $nextmonth->getTimestamp();
993         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nextmonthtime);
994         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
995         $this->assertTrue($data['result']);
996         $this->assertEmpty($data['errors']);
998         // Check updated user enrolment.
999         $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
1000         $this->assertEquals(ENROL_USER_ACTIVE, $ue->status);
1001         $this->assertEquals($nowtime, $ue->timestart);
1002         $this->assertEquals($nextmonthtime, $ue->timeend);
1004         // Suspend user.
1005         $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_SUSPENDED);
1006         $data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
1007         $this->assertTrue($data['result']);
1008         $this->assertEmpty($data['errors']);
1010         // Check updated user enrolment.
1011         $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
1012         $this->assertEquals(ENROL_USER_SUSPENDED, $ue->status);
1013     }
1015     /**
1016      * dataProvider for test_submit_user_enrolment_form().
1017      */
1018     public function submit_user_enrolment_form_provider() {
1019         $now = new DateTime();
1021         $nextmonth = clone($now);
1022         $nextmonth->add(new DateInterval('P1M'));
1024         return [
1025             'Invalid data' => [
1026                 'customdata' => [
1027                     'status' => ENROL_USER_ACTIVE,
1028                     'timestart' => [
1029                         'day' => $now->format('j'),
1030                         'month' => $now->format('n'),
1031                         'year' => $now->format('Y'),
1032                         'hour' => $now->format('G'),
1033                         'minute' => 0,
1034                         'enabled' => 1,
1035                     ],
1036                     'timeend' => [
1037                         'day' => $now->format('j'),
1038                         'month' => $now->format('n'),
1039                         'year' => $now->format('Y'),
1040                         'hour' => $now->format('G'),
1041                         'minute' => 0,
1042                         'enabled' => 1,
1043                     ],
1044                 ],
1045                 'expectedresult' => false,
1046                 'validationerror' => true,
1047             ],
1048             'Valid data' => [
1049                 'customdata' => [
1050                     'status' => ENROL_USER_ACTIVE,
1051                     'timestart' => [
1052                         'day' => $now->format('j'),
1053                         'month' => $now->format('n'),
1054                         'year' => $now->format('Y'),
1055                         'hour' => $now->format('G'),
1056                         'minute' => 0,
1057                         'enabled' => 1,
1058                     ],
1059                     'timeend' => [
1060                         'day' => $nextmonth->format('j'),
1061                         'month' => $nextmonth->format('n'),
1062                         'year' => $nextmonth->format('Y'),
1063                         'hour' => $nextmonth->format('G'),
1064                         'minute' => 0,
1065                         'enabled' => 1,
1066                     ],
1067                 ],
1068                 'expectedresult' => true,
1069                 'validationerror' => false
1070             ],
1071             'Suspend user' => [
1072                 'customdata' => [
1073                     'status' => ENROL_USER_SUSPENDED,
1074                 ],
1075                 'expectedresult' => true,
1076                 'validationerror' => false
1077             ],
1078         ];
1079     }
1081     /**
1082      * @param array $customdata The data we are providing to the webservice.
1083      * @param bool $expectedresult The result we are expecting to receive from the webservice.
1084      * @param bool $validationerror The validationerror we are expecting to receive from the webservice.
1085      * @dataProvider submit_user_enrolment_form_provider
1086      */
1087     public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror) {
1088         global $CFG, $DB;
1090         $this->resetAfterTest(true);
1091         $datagen = $this->getDataGenerator();
1093         /** @var enrol_manual_plugin $manualplugin */
1094         $manualplugin = enrol_get_plugin('manual');
1096         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1097         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1098         $course = $datagen->create_course();
1099         $user = $datagen->create_user();
1100         $teacher = $datagen->create_user();
1102         $instanceid = null;
1103         $instances = enrol_get_instances($course->id, true);
1104         foreach ($instances as $inst) {
1105             if ($inst->enrol == 'manual') {
1106                 $instanceid = (int)$inst->id;
1107                 break;
1108             }
1109         }
1110         if (empty($instanceid)) {
1111             $instanceid = $manualplugin->add_default_instance($course);
1112             if (empty($instanceid)) {
1113                 $instanceid = $manualplugin->add_instance($course);
1114             }
1115         }
1116         $this->assertNotNull($instanceid);
1118         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1119         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1120         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1121         $ueid = (int) $DB->get_field(
1122                 'user_enrolments',
1123                 'id',
1124                 ['enrolid' => $instance->id, 'userid' => $user->id],
1125                 MUST_EXIST
1126         );
1128         // Login as teacher.
1129         $teacher->ignoresesskey = true;
1130         $this->setUser($teacher);
1132         $formdata = [
1133             'ue'        => $ueid,
1134             'ifilter'   => 0,
1135             'status'    => null,
1136             'timestart' => null,
1137             'duration'  => null,
1138             'timeend'   => null,
1139         ];
1141         $formdata = array_merge($formdata, $customdata);
1143         require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1144         $formdata = enrol_user_enrolment_form::mock_generate_submit_keys($formdata);
1146         $querystring = http_build_query($formdata, '', '&');
1148         $result = external_api::clean_returnvalue(
1149                 core_enrol_external::submit_user_enrolment_form_returns(),
1150                 core_enrol_external::submit_user_enrolment_form($querystring)
1151         );
1153         $this->assertEqualsCanonicalizing(
1154                 ['result' => $expectedresult, 'validationerror' => $validationerror],
1155                 $result);
1157         if ($result['result']) {
1158             $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
1159             $this->assertEquals($formdata['status'], $ue->status);
1160         }
1161     }
1163     /**
1164      * Test for core_enrol_external::unenrol_user_enrolment().
1165      */
1166     public function test_unenerol_user_enrolment() {
1167         global $DB;
1169         $this->resetAfterTest(true);
1170         $datagen = $this->getDataGenerator();
1172         /** @var enrol_manual_plugin $manualplugin */
1173         $manualplugin = enrol_get_plugin('manual');
1174         $this->assertNotNull($manualplugin);
1176         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1177         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1178         $course = $datagen->create_course();
1179         $user = $datagen->create_user();
1180         $teacher = $datagen->create_user();
1182         $instanceid = null;
1183         $instances = enrol_get_instances($course->id, true);
1184         foreach ($instances as $inst) {
1185             if ($inst->enrol == 'manual') {
1186                 $instanceid = (int)$inst->id;
1187                 break;
1188             }
1189         }
1190         if (empty($instanceid)) {
1191             $instanceid = $manualplugin->add_default_instance($course);
1192             if (empty($instanceid)) {
1193                 $instanceid = $manualplugin->add_instance($course);
1194             }
1195         }
1196         $this->assertNotNull($instanceid);
1198         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1199         $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1200         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1201         $ueid = (int)$DB->get_field(
1202             'user_enrolments',
1203             'id',
1204             ['enrolid' => $instance->id, 'userid' => $user->id],
1205             MUST_EXIST
1206         );
1208         // Login as teacher.
1209         $this->setUser($teacher);
1211         // Invalid data by passing invalid ueid.
1212         $data = core_enrol_external::unenrol_user_enrolment(101010);
1213         $data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
1214         $this->assertFalse($data['result']);
1215         $this->assertNotEmpty($data['errors']);
1217         // Valid data.
1218         $data = core_enrol_external::unenrol_user_enrolment($ueid);
1219         $data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
1220         $this->assertTrue($data['result']);
1221         $this->assertEmpty($data['errors']);
1223         // Check unenrol user enrolment.
1224         $ue = $DB->count_records('user_enrolments', ['id' => $ueid]);
1225         $this->assertEquals(0, $ue);
1226     }
1228     /**
1229      * Test for core_enrol_external::test_search_users().
1230      */
1231     public function test_search_users() {
1232         global $DB;
1234         $this->resetAfterTest(true);
1235         $datagen = $this->getDataGenerator();
1237         /** @var enrol_manual_plugin $manualplugin */
1238         $manualplugin = enrol_get_plugin('manual');
1239         $this->assertNotNull($manualplugin);
1241         $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
1242         $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
1244         $course1 = $datagen->create_course();
1245         $course2 = $datagen->create_course();
1247         $user1 = $datagen->create_user(['firstname' => 'user 1']);
1248         $user2 = $datagen->create_user(['firstname' => 'user 2']);
1249         $user3 = $datagen->create_user(['firstname' => 'user 3']);
1250         $teacher = $datagen->create_user(['firstname' => 'user 4']);
1252         $instanceid = null;
1253         $instances = enrol_get_instances($course1->id, true);
1254         foreach ($instances as $inst) {
1255             if ($inst->enrol == 'manual') {
1256                 $instanceid = (int)$inst->id;
1257                 break;
1258             }
1259         }
1260         if (empty($instanceid)) {
1261             $instanceid = $manualplugin->add_default_instance($course1);
1262             if (empty($instanceid)) {
1263                 $instanceid = $manualplugin->add_instance($course1);
1264             }
1265         }
1266         $this->assertNotNull($instanceid);
1268         $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
1269         $manualplugin->enrol_user($instance, $user1->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1270         $manualplugin->enrol_user($instance, $user2->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1271         $manualplugin->enrol_user($instance, $user3->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
1272         $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
1274         $this->setUser($teacher);
1276         // Search for users in a course with enrolled users.
1277         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
1278         $this->assertCount(4, $result);
1280         $this->expectException('moodle_exception');
1281         // Search for users in a course without any enrolled users, shouldn't return anything.
1282         $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
1283         $this->assertCount(0, $result);
1285         // Search for invalid first name.
1286         $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
1287         $this->assertCount(0, $result);
1289         // Test pagination, it should return only 3 users.
1290         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 3);
1291         $this->assertCount(3, $result);
1293         // Test pagination, it should return only 3 users.
1294         $result = core_enrol_external::search_users($course1->id, 'user 1', true, 0, 1);
1295         $result = $result[0];
1296         $this->assertEquals($user1->id, $result['id']);
1297         $this->assertEquals($user1->email, $result['email']);
1298         $this->assertEquals(fullname($user1), $result['fullname']);
1300         $this->setUser($user1);
1302         // Search for users in a course with enrolled users.
1303         $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
1304         $this->assertCount(4, $result);
1306         $this->expectException('moodle_exception');
1307         // Search for users in a course without any enrolled users, shouldn't return anything.
1308         $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
1309         $this->assertCount(0, $result);
1311         // Search for invalid first name.
1312         $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
1313         $this->assertCount(0, $result);
1314     }