Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core_enrol; 18 19 use core_enrol_external; 20 use enrol_user_enrolment_form; 21 use externallib_advanced_testcase; 22 23 defined('MOODLE_INTERNAL') || die(); 24 25 global $CFG; 26 27 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 28 require_once($CFG->dirroot . '/enrol/externallib.php'); 29 30 /** 31 * Enrol external PHPunit tests 32 * 33 * @package core_enrol 34 * @category external 35 * @copyright 2012 Jerome Mouneyrac 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 * @since Moodle 2.4 38 */ 39 class externallib_test extends externallib_advanced_testcase { 40 41 /** 42 * dataProvider for test_get_enrolled_users_visibility(). 43 */ 44 public function get_enrolled_users_visibility_provider() { 45 return array( 46 'Course without groups, default behavior (not filtering by cap, group, active)' => 47 array( 48 'settings' => array( 49 'coursegroupmode' => NOGROUPS, 50 'withcapability' => null, 51 'groupid' => null, 52 'onlyactive' => false, 53 'allowedcaps' => array(), 54 ), 55 'results' => array( // Everybody can view everybody. 56 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 57 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 58 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 59 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 60 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 61 ), 62 ), 63 64 'Course with visible groups, default behavior (not filtering by cap, group, active)' => 65 array( 66 'settings' => array( 67 'coursegroupmode' => VISIBLEGROUPS, 68 'withcapability' => null, 69 'groupid' => null, 70 'onlyactive' => false, 71 'allowedcaps' => array(), 72 ), 73 'results' => array( // Everybody can view everybody. 74 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 75 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 76 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 77 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 78 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 79 ), 80 ), 81 82 'Course with separate groups, default behavior (not filtering by cap, group, active)' => 83 array( 84 'settings' => array( 85 'coursegroupmode' => SEPARATEGROUPS, 86 'withcapability' => null, 87 'groupid' => null, 88 'onlyactive' => false, 89 'allowedcaps' => array(), 90 ), 91 'results' => array( // Only users from own groups are visible. 92 'user0' => array('canview' => array()), // Poor guy, cannot see anybody, himself included. 93 'user1' => array('canview' => array('user1', 'userall')), 94 'user2' => array('canview' => array('user2', 'user2su', 'userall')), 95 'user31' => array('canview' => array('user31', 'user32', 'userall')), 96 'userall' => array('canview' => array('user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 97 ), 98 ), 99 100 'Course with separate groups, default behavior (not filtering but having moodle/site:accessallgroups)' => 101 array( 102 'settings' => array( 103 'coursegroupmode' => VISIBLEGROUPS, 104 'withcapability' => null, 105 'groupid' => null, 106 'onlyactive' => false, 107 'allowedcaps' => array('moodle/site:accessallgroups'), 108 ), 109 'results' => array( // Everybody can view everybody. 110 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 111 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 112 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 113 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 114 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')), 115 ), 116 ), 117 118 'Course with separate groups, filtering onlyactive (missing moodle/course:enrolreview)' => 119 array( 120 'settings' => array( 121 'coursegroupmode' => SEPARATEGROUPS, 122 'withcapability' => null, 123 'groupid' => null, 124 'onlyactive' => true, 125 'allowedcaps' => array(), 126 ), 127 'results' => array( // returns exception, cannot view anybody without the cap. 128 'user2' => array('exception' => array( 129 'type' => 'required_capability_exception', 130 'message' => 'Review course enrolments')), 131 'userall' => array('exception' => array( 132 'type' => 'required_capability_exception', 133 'message' => 'Review course enrolments')), 134 ), 135 ), 136 137 'Course with separate groups, filtering onlyactive (having moodle/course:enrolreview)' => 138 array( 139 'settings' => array( 140 'coursegroupmode' => SEPARATEGROUPS, 141 'withcapability' => null, 142 'groupid' => null, 143 'onlyactive' => true, 144 'allowedcaps' => array('moodle/course:enrolreview'), 145 ), 146 'results' => array( // Suspended are not returned. 147 'user2' => array('canview' => array('user2', 'userall')), 148 'user31' => array('canview' => array('user31', 'user32', 'userall')), 149 'userall' => array('canview' => array('user1', 'user2', 'user31', 'user32', 'userall')), 150 ), 151 ), 152 153 'Course with separate groups, filtering by groupid (not having moodle/site:accessallgroups)' => 154 array( 155 'settings' => array( 156 'coursegroupmode' => SEPARATEGROUPS, 157 'withcapability' => null, 158 'groupid' => 'group2', 159 'onlyactive' => false, 160 'allowedcaps' => array(), 161 ), 162 'results' => array( // Only group 2 members and only for members. Exception for non-members. 163 'user0' => array('exception' => array( 164 'type' => 'required_capability_exception', 165 'message' => 'Access all groups')), 166 'user1' => array('exception' => array( 167 'type' => 'required_capability_exception', 168 'message' => 'Access all groups')), 169 'user2' => array('canview' => array('user2', 'user2su', 'userall')), 170 'userall' => array('canview' => array('user2', 'user2su', 'userall')), 171 ), 172 ), 173 174 'Course with separate groups, filtering by groupid (having moodle/site:accessallgroups)' => 175 array( 176 'settings' => array( 177 'coursegroupmode' => SEPARATEGROUPS, 178 'withcapability' => null, 179 'groupid' => 'group2', 180 'onlyactive' => false, 181 'allowedcaps' => array('moodle/site:accessallgroups'), 182 ), 183 'results' => array( // All users with 'moodle/site:accessallgroups' can view group 2 184 'user0' => array('canview' => array('user2', 'user2su', 'userall')), 185 'user1' => array('canview' => array('user2', 'user2su', 'userall')), 186 'user2' => array('canview' => array('user2', 'user2su', 'userall')), 187 'userall' => array('canview' => array('user2', 'user2su', 'userall')), 188 ), 189 ), 190 191 'Course with separate groups, filtering by withcapability (not having moodle/role:review)' => 192 array( 193 'settings' => array( 194 'coursegroupmode' => SEPARATEGROUPS, 195 'withcapability' => 'moodle/course:bulkmessaging', 196 'groupid' => null, 197 'onlyactive' => false, 198 'allowedcaps' => array(), 199 ), 200 'results' => array( // No user has 'moodle/role:review' so exception. 201 'user0' => array('exception' => array( 202 'type' => 'required_capability_exception', 203 'message' => 'Review permissions for others')), 204 'user1' => array('exception' => array( 205 'type' => 'required_capability_exception', 206 'message' => 'Review permissions for others')), 207 'user2' => array('exception' => array( 208 'type' => 'required_capability_exception', 209 'message' => 'Review permissions for others')), 210 'userall' => array('exception' => array( 211 'type' => 'required_capability_exception', 212 'message' => 'Review permissions for others')), 213 ), 214 ), 215 216 'Course with separate groups, filtering by withcapability (having moodle/role:review)' => 217 array( 218 'settings' => array( 219 'coursegroupmode' => SEPARATEGROUPS, 220 'withcapability' => 'moodle/course:bulkmessaging', 221 'groupid' => null, 222 'onlyactive' => false, 223 'allowedcaps' => array('moodle/role:review'), 224 ), 225 'results' => array( // No user has withcapability, but all have 'moodle/role:review'. Empties. 226 'user0' => array('canview' => array()), 227 'user1' => array('canview' => array()), 228 'user2' => array('canview' => array()), 229 'userall' => array('canview' => array()), 230 ), 231 ), 232 233 'Course with separate groups, filtering by withcapability (having moodle/role:review)' => 234 array( 235 'settings' => array( 236 'coursegroupmode' => SEPARATEGROUPS, 237 'withcapability' => 'moodle/course:bulkmessaging', 238 'groupid' => null, 239 'onlyactive' => false, 240 'allowedcaps' => array('moodle/role:review', 'moodle/course:bulkmessaging'), 241 ), 242 'results' => array( // Users (previous) have withcapability, and all have 'moodle/role:review'. 243 'user0' => array('canview' => array()), 244 'user1' => array('canview' => array('user1')), 245 'user2' => array('canview' => array('user2')), 246 'userall' => array('canview' => array('user1', 'user2', 'userall')), 247 ), 248 ), 249 ); 250 } 251 252 /** 253 * Verify get_enrolled_users() returned users are the expected in every situation. 254 * 255 * @dataProvider get_enrolled_users_visibility_provider 256 */ 257 public function test_get_enrolled_users_visibility($settings, $results) { 258 259 global $USER; 260 261 $this->resetAfterTest(); 262 263 // Create the course and the users. 264 $course = $this->getDataGenerator()->create_course(array('groupmode' => $settings['coursegroupmode'])); 265 $coursecontext = \context_course::instance($course->id); 266 $user0 = $this->getDataGenerator()->create_user(array('username' => 'user0')); // A user without group. 267 $user1 = $this->getDataGenerator()->create_user(array('username' => 'user1')); // User for group 1. 268 $user2 = $this->getDataGenerator()->create_user(array('username' => 'user2')); // Two users for group 2. 269 $user2su = $this->getDataGenerator()->create_user(array('username' => 'user2su')); // (one suspended). 270 $user31 = $this->getDataGenerator()->create_user(array('username' => 'user31')); // Two users for group 3. 271 $user32 = $this->getDataGenerator()->create_user(array('username' => 'user32')); // (both enabled). 272 $userall = $this->getDataGenerator()->create_user(array('username' => 'userall')); // A user in all groups. 273 274 // Create utility array of created users, to produce better assertion messages. 275 $createdusers = array(); 276 foreach (array($user0, $user1, $user2, $user2su, $user31, $user32, $userall) as $createduser) { 277 $createdusers[$createduser->id] = $createduser->username; 278 } 279 280 // Enrol the users in the course. 281 $this->getDataGenerator()->enrol_user($user0->id, $course->id); 282 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 283 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 284 $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED); 285 $this->getDataGenerator()->enrol_user($user31->id, $course->id); 286 $this->getDataGenerator()->enrol_user($user32->id, $course->id); 287 $this->getDataGenerator()->enrol_user($userall->id, $course->id); 288 289 // Create 3 groups. 290 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 291 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 292 $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 293 294 // Add the users to the groups. 295 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); 296 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id)); 297 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2su->id)); 298 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user31->id)); 299 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user32->id)); 300 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $userall->id)); 301 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $userall->id)); 302 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $userall->id)); 303 304 // Create a role to add the allowedcaps. Users will have this role assigned. 305 $roleid = $this->getDataGenerator()->create_role(); 306 // Allow the specified capabilities. 307 if (!empty($settings['allowedcaps'])) { 308 foreach ($settings['allowedcaps'] as $capability) { 309 assign_capability($capability, CAP_ALLOW, $roleid, $coursecontext); 310 } 311 } 312 313 // For each of the users, configure everything, perform the call, and assert results. 314 foreach ($results as $user => $expectations) { 315 // Convert canview expectations into a nice array of ids for easier handling. 316 $canview = array(); 317 $exception = null; 318 // Analyse the expectations. 319 if (isset($expectations['canview'])) { 320 foreach ($expectations['canview'] as $canviewuser) { 321 $canview[] = $createdusers[${$canviewuser}->id]; 322 } 323 } else if (isset($expectations['exception'])) { 324 $exception = $expectations['exception']; 325 $this->expectException($exception['type']); 326 $this->expectExceptionMessage($exception['message']); 327 } else { 328 // Failed, only canview and exception are supported. 329 $this->markTestIncomplete('Incomplete, only canview and exception are supported'); 330 } 331 // Switch to the user and assign the role. 332 $this->setUser(${$user}); 333 role_assign($roleid, $USER->id, $coursecontext); 334 335 // Convert groupid to proper id. 336 $groupid = 0; 337 if (isset($settings['groupid'])) { 338 $groupid = ${$settings['groupid']}->id; 339 } 340 341 // Call to the function. 342 $options = array( 343 array('name' => 'withcapability', 'value' => $settings['withcapability']), 344 array('name' => 'groupid', 'value' => $groupid), 345 array('name' => 'onlyactive', 'value' => $settings['onlyactive']), 346 array('name' => 'userfields', 'value' => 'id') 347 ); 348 $enrolledusers = core_enrol_external::get_enrolled_users($course->id, $options); 349 350 // We need to execute the return values cleaning process to simulate the web service server. 351 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 352 353 // We are only interested in ids to check visibility. 354 $viewed = array(); 355 // Verify the user canview the expected users. 356 foreach ($enrolledusers as $enrolleduser) { 357 $viewed[] = $createdusers[$enrolleduser['id']]; 358 } 359 // Verify viewed matches canview expectation (using canonicalize to ignore ordering). 360 $this->assertEqualsCanonicalizing($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'"); 361 } 362 } 363 364 /** 365 * Verify get_enrolled_users() returned users according to their status. 366 */ 367 public function test_get_enrolled_users_active_suspended() { 368 global $USER; 369 370 $this->resetAfterTest(); 371 372 // Create the course and the users. 373 $course = $this->getDataGenerator()->create_course(); 374 $coursecontext = \context_course::instance($course->id); 375 $user0 = $this->getDataGenerator()->create_user(['username' => 'user0active']); 376 $user1 = $this->getDataGenerator()->create_user(['username' => 'user1active']); 377 $user2 = $this->getDataGenerator()->create_user(['username' => 'user2active']); 378 $user2su = $this->getDataGenerator()->create_user(['username' => 'user2suspended']); // Suspended user. 379 $user3 = $this->getDataGenerator()->create_user(['username' => 'user3active']); 380 $user3su = $this->getDataGenerator()->create_user(['username' => 'user3suspended']); // Suspended user. 381 382 // Enrol the users in the course. 383 $this->getDataGenerator()->enrol_user($user0->id, $course->id); 384 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 385 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 386 $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED); 387 $this->getDataGenerator()->enrol_user($user3->id, $course->id); 388 $this->getDataGenerator()->enrol_user($user3su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED); 389 390 // Create a role to add the allowedcaps. Users will have this role assigned. 391 $roleid = $this->getDataGenerator()->create_role(); 392 // Allow the specified capabilities. 393 assign_capability('moodle/course:enrolreview', CAP_ALLOW, $roleid, $coursecontext); 394 assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $roleid, $coursecontext); 395 396 // Switch to the user and assign the role. 397 $this->setUser($user0); 398 role_assign($roleid, $USER->id, $coursecontext); 399 400 // Suspended users. 401 $options = [ 402 ['name' => 'onlysuspended', 'value' => true], 403 ['name' => 'userfields', 'value' => 'id,username'] 404 ]; 405 $suspendedusers = core_enrol_external::get_enrolled_users($course->id, $options); 406 407 // We need to execute the return values cleaning process to simulate the web service server. 408 $suspendedusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $suspendedusers); 409 $this->assertCount(2, $suspendedusers); 410 411 foreach ($suspendedusers as $suspendeduser) { 412 $this->assertStringContainsString('suspended', $suspendeduser['username']); 413 } 414 415 // Active users. 416 $options = [ 417 ['name' => 'onlyactive', 'value' => true], 418 ['name' => 'userfields', 'value' => 'id,username'] 419 ]; 420 $activeusers = core_enrol_external::get_enrolled_users($course->id, $options); 421 422 // We need to execute the return values cleaning process to simulate the web service server. 423 $activeusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $activeusers); 424 $this->assertCount(4, $activeusers); 425 426 foreach ($activeusers as $activeuser) { 427 $this->assertStringContainsString('active', $activeuser['username']); 428 } 429 430 // All enrolled users. 431 $options = [ 432 ['name' => 'userfields', 'value' => 'id,username'] 433 ]; 434 $allusers = core_enrol_external::get_enrolled_users($course->id, $options); 435 436 // We need to execute the return values cleaning process to simulate the web service server. 437 $allusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $allusers); 438 $this->assertCount(6, $allusers); 439 440 // Active and suspended. Test exception is thrown. 441 $options = [ 442 ['name' => 'onlyactive', 'value' => true], 443 ['name' => 'onlysuspended', 'value' => true], 444 ['name' => 'userfields', 'value' => 'id,username'] 445 ]; 446 $this->expectException('coding_exception'); 447 $message = 'Coding error detected, it must be fixed by a programmer: Both onlyactive ' . 448 'and onlysuspended are set, this is probably not what you want!'; 449 $this->expectExceptionMessage($message); 450 core_enrol_external::get_enrolled_users($course->id, $options); 451 } 452 453 /** 454 * Test get_users_courses 455 */ 456 public function test_get_users_courses() { 457 global $CFG, $DB; 458 require_once($CFG->dirroot . '/completion/criteria/completion_criteria_self.php'); 459 460 $this->resetAfterTest(true); 461 $CFG->enablecompletion = 1; 462 463 $timenow = time(); 464 $coursedata1 = array( 465 'fullname' => '<b>Course 1</b>', // Adding tags here to check that external_format_string works. 466 'shortname' => '<b>Course 1</b>', // Adding tags here to check that external_format_string works. 467 'summary' => 'Lightwork Course 1 description', 468 'summaryformat' => FORMAT_MOODLE, 469 'lang' => 'en', 470 'enablecompletion' => true, 471 'showgrades' => true, 472 'startdate' => $timenow, 473 'enddate' => $timenow + WEEKSECS, 474 'marker' => 1 475 ); 476 477 $coursedata2 = array( 478 'lang' => 'kk', // Check invalid language pack. 479 ); 480 481 $course1 = self::getDataGenerator()->create_course($coursedata1); 482 $course2 = self::getDataGenerator()->create_course($coursedata2); 483 $courses = array($course1, $course2); 484 $contexts = array ($course1->id => \context_course::instance($course1->id), 485 $course2->id => \context_course::instance($course2->id)); 486 487 $student = $this->getDataGenerator()->create_user(); 488 $otherstudent = $this->getDataGenerator()->create_user(); 489 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); 490 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentroleid); 491 $this->getDataGenerator()->enrol_user($otherstudent->id, $course1->id, $studentroleid); 492 $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentroleid); 493 494 // Force last access. 495 $timenow = time(); 496 $lastaccess = array( 497 'userid' => $student->id, 498 'courseid' => $course1->id, 499 'timeaccess' => $timenow 500 ); 501 $DB->insert_record('user_lastaccess', $lastaccess); 502 503 // Force completion, setting at least one criteria. 504 require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php'); 505 $criteriadata = new \stdClass(); 506 $criteriadata->id = $course1->id; 507 // Self completion. 508 $criteriadata->criteria_self = 1; 509 510 $criterion = new \completion_criteria_self(); 511 $criterion->update_config($criteriadata); 512 513 $ccompletion = new \completion_completion(array('course' => $course1->id, 'userid' => $student->id)); 514 $ccompletion->mark_complete(); 515 516 // Set course hidden and favourited. 517 set_user_preference('block_myoverview_hidden_course_' . $course1->id, 1, $student); 518 $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($student->id)); 519 $ufservice->create_favourite('core_course', 'courses', $course1->id, \context_system::instance()); 520 521 $this->setUser($student); 522 // Call the external function. 523 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 524 525 // We need to execute the return values cleaning process to simulate the web service server. 526 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 527 528 // Check we retrieve the good total number of enrolled users. 529 $this->assertEquals(2, count($enrolledincourses)); 530 531 // We need to format summary and summaryformat before to compare them with those values returned by the webservice. 532 list($course1->summary, $course1->summaryformat) = 533 external_format_text($course1->summary, $course1->summaryformat, $contexts[$course1->id]->id, 'course', 'summary', 0); 534 535 // Check there are no differences between $course1 properties and course values returned by the webservice 536 // only for those fields listed in the $coursedata1 array. 537 $course1->fullname = external_format_string($course1->fullname, $contexts[$course1->id]->id); 538 $course1->shortname = external_format_string($course1->shortname, $contexts[$course1->id]->id); 539 foreach ($enrolledincourses as $courseenrol) { 540 if ($courseenrol['id'] == $course1->id) { 541 foreach ($coursedata1 as $fieldname => $value) { 542 $this->assertEquals($courseenrol[$fieldname], $course1->$fieldname); 543 } 544 // Text extra fields. 545 $this->assertEquals($course1->fullname, $courseenrol['displayname']); 546 $this->assertEquals([], $courseenrol['overviewfiles']); 547 $this->assertEquals($timenow, $courseenrol['lastaccess']); 548 $this->assertEquals(100.0, $courseenrol['progress']); 549 $this->assertEquals(true, $courseenrol['completed']); 550 $this->assertTrue($courseenrol['completionhascriteria']); 551 $this->assertTrue($courseenrol['completionusertracked']); 552 $this->assertTrue($courseenrol['hidden']); 553 $this->assertTrue($courseenrol['isfavourite']); 554 $this->assertEquals(2, $courseenrol['enrolledusercount']); 555 } else { 556 // Check language pack. Should be empty since an incorrect one was used when creating the course. 557 $this->assertEmpty($courseenrol['lang']); 558 $this->assertEquals($course2->fullname, $courseenrol['displayname']); 559 $this->assertEquals([], $courseenrol['overviewfiles']); 560 $this->assertEquals(0, $courseenrol['lastaccess']); 561 $this->assertEquals(0, $courseenrol['progress']); 562 $this->assertEquals(false, $courseenrol['completed']); 563 $this->assertFalse($courseenrol['completionhascriteria']); 564 $this->assertFalse($courseenrol['completionusertracked']); 565 $this->assertFalse($courseenrol['hidden']); 566 $this->assertFalse($courseenrol['isfavourite']); 567 $this->assertEquals(1, $courseenrol['enrolledusercount']); 568 } 569 } 570 571 // Check that returnusercount works correctly. 572 $enrolledincourses = core_enrol_external::get_users_courses($student->id, false); 573 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 574 foreach ($enrolledincourses as $courseenrol) { 575 $this->assertFalse(isset($courseenrol['enrolledusercount'])); 576 } 577 578 // Now check that admin users can see all the info. 579 $this->setAdminUser(); 580 581 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 582 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 583 $this->assertEquals(2, count($enrolledincourses)); 584 foreach ($enrolledincourses as $courseenrol) { 585 if ($courseenrol['id'] == $course1->id) { 586 $this->assertEquals($timenow, $courseenrol['lastaccess']); 587 $this->assertEquals(100.0, $courseenrol['progress']); 588 $this->assertTrue($courseenrol['completionhascriteria']); 589 $this->assertTrue($courseenrol['completionusertracked']); 590 $this->assertFalse($courseenrol['isfavourite']); // This always false. 591 $this->assertFalse($courseenrol['hidden']); // This always false. 592 } else { 593 $this->assertEquals(0, $courseenrol['progress']); 594 $this->assertFalse($courseenrol['completionhascriteria']); 595 $this->assertFalse($courseenrol['completionusertracked']); 596 $this->assertFalse($courseenrol['isfavourite']); // This always false. 597 $this->assertFalse($courseenrol['hidden']); // This always false. 598 } 599 } 600 601 // Check other users can't see private info. 602 $this->setUser($otherstudent); 603 604 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 605 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 606 $this->assertEquals(1, count($enrolledincourses)); 607 608 $this->assertEquals($timenow, $enrolledincourses[0]['lastaccess']); // I can see this, not hidden. 609 $this->assertEquals(null, $enrolledincourses[0]['progress']); // I can't see this, private. 610 611 // Change some global profile visibility fields. 612 $CFG->hiddenuserfields = 'lastaccess'; 613 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 614 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 615 616 $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting. 617 } 618 619 /** 620 * Test that get_users_courses respects the capability to view participants when viewing courses of other user 621 */ 622 public function test_get_users_courses_can_view_participants(): void { 623 global $DB; 624 625 $this->resetAfterTest(); 626 627 $course = $this->getDataGenerator()->create_course(); 628 $context = \context_course::instance($course->id); 629 630 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 631 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 632 633 $this->setUser($user1); 634 635 $courses = core_enrol_external::clean_returnvalue( 636 core_enrol_external::get_users_courses_returns(), 637 core_enrol_external::get_users_courses($user2->id, false) 638 ); 639 640 $this->assertCount(1, $courses); 641 $this->assertEquals($course->id, reset($courses)['id']); 642 643 // Prohibit the capability for viewing course participants. 644 $studentrole = $DB->get_field('role', 'id', ['shortname' => 'student']); 645 assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT, $studentrole, $context->id); 646 647 $courses = core_enrol_external::clean_returnvalue( 648 core_enrol_external::get_users_courses_returns(), 649 core_enrol_external::get_users_courses($user2->id, false) 650 ); 651 $this->assertEmpty($courses); 652 } 653 654 /* 655 * Test that get_users_courses respects the capability to view a users profile when viewing courses of other user 656 */ 657 public function test_get_users_courses_can_view_profile(): void { 658 $this->resetAfterTest(); 659 660 $course = $this->getDataGenerator()->create_course([ 661 'groupmode' => VISIBLEGROUPS, 662 ]); 663 664 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 665 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 666 667 // Create separate groups for each of our students. 668 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 669 groups_add_member($group1, $user1); 670 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 671 groups_add_member($group2, $user2); 672 673 $this->setUser($user1); 674 675 $courses = core_enrol_external::clean_returnvalue( 676 core_enrol_external::get_users_courses_returns(), 677 core_enrol_external::get_users_courses($user2->id, false) 678 ); 679 680 $this->assertCount(1, $courses); 681 $this->assertEquals($course->id, reset($courses)['id']); 682 683 // Change to separate groups mode, so students can't view information about each other in different groups. 684 $course->groupmode = SEPARATEGROUPS; 685 update_course($course); 686 687 $courses = core_enrol_external::clean_returnvalue( 688 core_enrol_external::get_users_courses_returns(), 689 core_enrol_external::get_users_courses($user2->id, false) 690 ); 691 $this->assertEmpty($courses); 692 } 693 694 /** 695 * Test get_users_courses with mathjax in the name. 696 */ 697 public function test_get_users_courses_with_mathjax() { 698 global $DB; 699 700 $this->resetAfterTest(true); 701 702 // Enable MathJax filter in content and headings. 703 $this->configure_filters([ 704 ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], 705 ]); 706 707 // Create a course with MathJax in the name and summary. 708 $coursedata = [ 709 'fullname' => 'Course 1 $$(a+b)=2$$', 710 'shortname' => 'Course 1 $$(a+b)=2$$', 711 'summary' => 'Lightwork Course 1 description $$(a+b)=2$$', 712 'summaryformat' => FORMAT_HTML, 713 ]; 714 715 $course = self::getDataGenerator()->create_course($coursedata); 716 $context = \context_course::instance($course->id); 717 718 // Enrol a student in the course. 719 $student = $this->getDataGenerator()->create_user(); 720 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']); 721 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentroleid); 722 723 $this->setUser($student); 724 725 // Call the external function. 726 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 727 728 // We need to execute the return values cleaning process to simulate the web service server. 729 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 730 731 // Check that the amount of courses is the right one. 732 $this->assertCount(1, $enrolledincourses); 733 734 // Filter the values to compare them with the returned ones. 735 $course->fullname = external_format_string($course->fullname, $context->id); 736 $course->shortname = external_format_string($course->shortname, $context->id); 737 list($course->summary, $course->summaryformat) = 738 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0); 739 740 // Compare the values. 741 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['fullname']); 742 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['shortname']); 743 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['summary']); 744 $this->assertEquals($course->fullname, $enrolledincourses[0]['fullname']); 745 $this->assertEquals($course->shortname, $enrolledincourses[0]['shortname']); 746 $this->assertEquals($course->summary, $enrolledincourses[0]['summary']); 747 } 748 749 /** 750 * Test get_course_enrolment_methods 751 */ 752 public function test_get_course_enrolment_methods() { 753 global $DB; 754 755 $this->resetAfterTest(true); 756 757 // Get enrolment plugins. 758 $selfplugin = enrol_get_plugin('self'); 759 $this->assertNotEmpty($selfplugin); 760 $manualplugin = enrol_get_plugin('manual'); 761 $this->assertNotEmpty($manualplugin); 762 763 $studentrole = $DB->get_record('role', array('shortname'=>'student')); 764 $this->assertNotEmpty($studentrole); 765 766 $course1 = self::getDataGenerator()->create_course(); 767 $coursedata = new \stdClass(); 768 $coursedata->visible = 0; 769 $course2 = self::getDataGenerator()->create_course($coursedata); 770 771 // Add enrolment methods for course. 772 $instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED, 773 'name' => 'Test instance 1', 774 'customint6' => 1, 775 'roleid' => $studentrole->id)); 776 $instanceid2 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_DISABLED, 777 'name' => 'Test instance 2', 778 'roleid' => $studentrole->id)); 779 780 $instanceid3 = $manualplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED, 781 'name' => 'Test instance 3')); 782 783 $enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id, 'status' => ENROL_INSTANCE_ENABLED)); 784 $this->assertCount(2, $enrolmentmethods); 785 786 $this->setAdminUser(); 787 788 // Check if information is returned. 789 $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course1->id); 790 $enrolmentmethods = \external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(), 791 $enrolmentmethods); 792 // Enrolment information is currently returned by self enrolment plugin, so count == 1. 793 // This should be changed as we implement get_enrol_info() for other enrolment plugins. 794 $this->assertCount(1, $enrolmentmethods); 795 796 $enrolmentmethod = $enrolmentmethods[0]; 797 $this->assertEquals($course1->id, $enrolmentmethod['courseid']); 798 $this->assertEquals('self', $enrolmentmethod['type']); 799 $this->assertTrue($enrolmentmethod['status']); 800 $this->assertFalse(isset($enrolmentmethod['wsfunction'])); 801 802 $instanceid4 = $selfplugin->add_instance($course2, array('status' => ENROL_INSTANCE_ENABLED, 803 'name' => 'Test instance 4', 804 'roleid' => $studentrole->id, 805 'customint6' => 1, 806 'password' => 'test')); 807 $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course2->id); 808 $enrolmentmethods = \external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(), 809 $enrolmentmethods); 810 $this->assertCount(1, $enrolmentmethods); 811 812 $enrolmentmethod = $enrolmentmethods[0]; 813 $this->assertEquals($course2->id, $enrolmentmethod['courseid']); 814 $this->assertEquals('self', $enrolmentmethod['type']); 815 $this->assertTrue($enrolmentmethod['status']); 816 $this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']); 817 818 // Try to retrieve information using a normal user for a hidden course. 819 $user = self::getDataGenerator()->create_user(); 820 $this->setUser($user); 821 try { 822 core_enrol_external::get_course_enrolment_methods($course2->id); 823 } catch (\moodle_exception $e) { 824 $this->assertEquals('coursehidden', $e->errorcode); 825 } 826 } 827 828 public function get_enrolled_users_setup($capability) { 829 global $USER; 830 831 $this->resetAfterTest(true); 832 833 $return = new \stdClass(); 834 835 $return->course = self::getDataGenerator()->create_course(); 836 $return->user1 = self::getDataGenerator()->create_user(); 837 $return->user2 = self::getDataGenerator()->create_user(); 838 $return->user3 = self::getDataGenerator()->create_user(); 839 $this->setUser($return->user3); 840 841 // Set the required capabilities by the external function. 842 $return->context = \context_course::instance($return->course->id); 843 $return->roleid = $this->assignUserCapability($capability, $return->context->id); 844 $this->assignUserCapability('moodle/user:viewdetails', $return->context->id, $return->roleid); 845 846 // Enrol the users in the course. 847 $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual'); 848 $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual'); 849 $this->getDataGenerator()->enrol_user($return->user3->id, $return->course->id, $return->roleid, 'manual'); 850 851 return $return; 852 } 853 854 /** 855 * Test get_enrolled_users from core_enrol_external without additional 856 * parameters. 857 */ 858 public function test_get_enrolled_users_without_parameters() { 859 $capability = 'moodle/course:viewparticipants'; 860 $data = $this->get_enrolled_users_setup($capability); 861 862 // Call the external function. 863 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 864 865 // We need to execute the return values cleaning process to simulate the web service server. 866 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 867 868 // Check the result set. 869 $this->assertEquals(3, count($enrolledusers)); 870 $this->assertArrayHasKey('email', $enrolledusers[0]); 871 } 872 873 /** 874 * Test get_enrolled_users from core_enrol_external with some parameters set. 875 */ 876 public function test_get_enrolled_users_with_parameters() { 877 $capability = 'moodle/course:viewparticipants'; 878 $data = $this->get_enrolled_users_setup($capability); 879 880 // Call the function with some parameters set. 881 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id, array( 882 array('name' => 'limitfrom', 'value' => 2), 883 array('name' => 'limitnumber', 'value' => 1), 884 array('name' => 'userfields', 'value' => 'id') 885 )); 886 887 // We need to execute the return values cleaning process to simulate the web service server. 888 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 889 890 // Check the result set, we should only get the 3rd result, which is $user3. 891 $this->assertCount(1, $enrolledusers); 892 $this->assertEquals($data->user3->id, $enrolledusers[0]['id']); 893 $this->assertArrayHasKey('id', $enrolledusers[0]); 894 $this->assertArrayNotHasKey('email', $enrolledusers[0]); 895 } 896 897 898 /** 899 * Test get_enrolled_users last course access. 900 */ 901 public function test_get_enrolled_users_including_lastcourseaccess() { 902 global $DB; 903 $capability = 'moodle/course:viewparticipants'; 904 $data = $this->get_enrolled_users_setup($capability); 905 906 // Call the external function. 907 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 908 // We need to execute the return values cleaning process to simulate the web service server. 909 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 910 911 // Check the result set. 912 $this->assertEquals(3, count($enrolledusers)); 913 $this->assertArrayHasKey('email', $enrolledusers[0]); 914 $this->assertEquals(0, $enrolledusers[0]['lastcourseaccess']); 915 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']); 916 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']); // We forced an access to the course via setUser. 917 918 // Force last access. 919 $timenow = time(); 920 $lastaccess = array( 921 'userid' => $enrolledusers[0]['id'], 922 'courseid' => $data->course->id, 923 'timeaccess' => $timenow 924 ); 925 $DB->insert_record('user_lastaccess', $lastaccess); 926 927 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 928 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 929 930 // Check the result set. 931 $this->assertEquals(3, count($enrolledusers)); 932 $this->assertEquals($timenow, $enrolledusers[0]['lastcourseaccess']); 933 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']); 934 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']); 935 } 936 937 /** 938 * Test get_enrolled_users from core_enrol_external with capability to 939 * viewparticipants removed. 940 */ 941 public function test_get_enrolled_users_without_capability() { 942 $capability = 'moodle/course:viewparticipants'; 943 $data = $this->get_enrolled_users_setup($capability); 944 945 // Call without required capability. 946 $this->unassignUserCapability($capability, $data->context->id, $data->roleid); 947 $this->expectException(\moodle_exception::class); 948 $categories = core_enrol_external::get_enrolled_users($data->course->id); 949 } 950 951 public function get_enrolled_users_with_capability_setup($capability) { 952 global $USER, $DB; 953 954 $this->resetAfterTest(true); 955 956 $return = new \stdClass(); 957 958 // Create the course and fetch its context. 959 $return->course = self::getDataGenerator()->create_course(); 960 $context = \context_course::instance($return->course->id); 961 962 // Create one teacher, and two students. 963 $return->teacher = self::getDataGenerator()->create_user(); 964 $return->student1 = self::getDataGenerator()->create_user(); 965 $return->student2 = self::getDataGenerator()->create_user(); 966 967 // Create a new student role based on the student archetype but with the capability prohibitted. 968 $fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student'); 969 assign_capability($capability, CAP_PROHIBIT, $fakestudentroleid, $context->id); 970 971 // Enrol all of the users in the course. 972 // * 'teacher' is an editing teacher. 973 // * 'student1' is a standard student. 974 // * 'student2' is a student with the capability prohibitted. 975 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); 976 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); 977 $this->getDataGenerator()->enrol_user($return->teacher->id, $return->course->id, $editingteacherroleid); 978 $this->getDataGenerator()->enrol_user($return->student1->id, $return->course->id, $studentroleid); 979 $this->getDataGenerator()->enrol_user($return->student2->id, $return->course->id, $fakestudentroleid); 980 981 // Log in as the teacher. 982 $this->setUser($return->teacher); 983 984 // Clear caches. 985 accesslib_clear_all_caches_for_unit_testing(); 986 987 return $return; 988 } 989 990 /** 991 * Test get_enrolled_users_with_capability without additional paramaters. 992 */ 993 public function test_get_enrolled_users_with_capability_without_parameters() { 994 $capability = 'moodle/course:viewparticipants'; 995 $data = $this->get_enrolled_users_with_capability_setup($capability); 996 997 $result = core_enrol_external::get_enrolled_users_with_capability( 998 array( 999 'coursecapabilities' => array( 1000 'courseid' => $data->course->id, 1001 'capabilities' => array( 1002 $capability, 1003 ), 1004 ), 1005 ), 1006 array() 1007 ); 1008 1009 // We need to execute the return values cleaning process to simulate the web service server. 1010 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1011 1012 // Check an array containing the expected user for the course capability is returned. 1013 $expecteduserlist = $result[0]; 1014 $this->assertEquals($data->course->id, $expecteduserlist['courseid']); 1015 $this->assertEquals($capability, $expecteduserlist['capability']); 1016 $this->assertEquals(2, count($expecteduserlist['users'])); 1017 } 1018 1019 /** 1020 * Test get_enrolled_users_with_capability 1021 */ 1022 public function test_get_enrolled_users_with_capability_with_parameters () { 1023 $capability = 'moodle/course:viewparticipants'; 1024 $data = $this->get_enrolled_users_with_capability_setup($capability); 1025 1026 $result = core_enrol_external::get_enrolled_users_with_capability( 1027 array( 1028 'coursecapabilities' => array( 1029 'courseid' => $data->course->id, 1030 'capabilities' => array( 1031 $capability, 1032 ), 1033 ), 1034 ), 1035 array( 1036 array('name' => 'limitfrom', 'value' => 1), 1037 array('name' => 'limitnumber', 'value' => 1), 1038 array('name' => 'userfields', 'value' => 'id') 1039 ) 1040 ); 1041 1042 // We need to execute the return values cleaning process to simulate the web service server. 1043 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1044 1045 // Check an array containing the expected user for the course capability is returned. 1046 $expecteduserlist = $result[0]['users']; 1047 $expecteduser = reset($expecteduserlist); 1048 $this->assertEquals(1, count($expecteduserlist)); 1049 $this->assertEquals($data->student1->id, $expecteduser['id']); 1050 } 1051 1052 /** 1053 * Test get_enrolled_users last course access. 1054 */ 1055 public function test_get_enrolled_users_with_capability_including_lastcourseaccess() { 1056 global $DB; 1057 $capability = 'moodle/course:viewparticipants'; 1058 $data = $this->get_enrolled_users_with_capability_setup($capability); 1059 1060 $parameters = array( 1061 'coursecapabilities' => array( 1062 'courseid' => $data->course->id, 1063 'capabilities' => array( 1064 $capability, 1065 ), 1066 ), 1067 ); 1068 1069 $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array()); 1070 // We need to execute the return values cleaning process to simulate the web service server. 1071 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1072 1073 // Check an array containing the expected user for the course capability is returned. 1074 $expecteduserlist = $result[0]; 1075 $this->assertEquals($data->course->id, $expecteduserlist['courseid']); 1076 $this->assertEquals($capability, $expecteduserlist['capability']); 1077 $this->assertEquals(2, count($expecteduserlist['users'])); 1078 // We forced an access to the course via setUser. 1079 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']); 1080 $this->assertEquals(0, $expecteduserlist['users'][1]['lastcourseaccess']); 1081 1082 // Force last access. 1083 $timenow = time(); 1084 $lastaccess = array( 1085 'userid' => $expecteduserlist['users'][1]['id'], 1086 'courseid' => $data->course->id, 1087 'timeaccess' => $timenow 1088 ); 1089 $DB->insert_record('user_lastaccess', $lastaccess); 1090 1091 $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array()); 1092 // We need to execute the return values cleaning process to simulate the web service server. 1093 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1094 1095 // Check the result set. 1096 $expecteduserlist = $result[0]; 1097 $this->assertEquals(2, count($expecteduserlist['users'])); 1098 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']); 1099 $this->assertEquals($timenow, $expecteduserlist['users'][1]['lastcourseaccess']); 1100 } 1101 1102 /** 1103 * Test for core_enrol_external::edit_user_enrolment(). 1104 */ 1105 public function test_edit_user_enrolment() { 1106 global $DB; 1107 1108 $this->resetAfterTest(true); 1109 $datagen = $this->getDataGenerator(); 1110 1111 /** @var enrol_manual_plugin $manualplugin */ 1112 $manualplugin = enrol_get_plugin('manual'); 1113 $this->assertNotNull($manualplugin); 1114 1115 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1116 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1117 $course = $datagen->create_course(); 1118 $user = $datagen->create_user(); 1119 $teacher = $datagen->create_user(); 1120 1121 $instanceid = null; 1122 $instances = enrol_get_instances($course->id, true); 1123 foreach ($instances as $inst) { 1124 if ($inst->enrol == 'manual') { 1125 $instanceid = (int)$inst->id; 1126 break; 1127 } 1128 } 1129 if (empty($instanceid)) { 1130 $instanceid = $manualplugin->add_default_instance($course); 1131 if (empty($instanceid)) { 1132 $instanceid = $manualplugin->add_instance($course); 1133 } 1134 } 1135 $this->assertNotNull($instanceid); 1136 1137 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1138 $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1139 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1140 $ueid = (int)$DB->get_field( 1141 'user_enrolments', 1142 'id', 1143 ['enrolid' => $instance->id, 'userid' => $user->id], 1144 MUST_EXIST 1145 ); 1146 1147 // Login as teacher. 1148 $this->setUser($teacher); 1149 1150 $now = new \DateTime(); 1151 $nowtime = $now->getTimestamp(); 1152 1153 // Invalid data. 1154 $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nowtime); 1155 $data = \external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data); 1156 $this->assertFalse($data['result']); 1157 $this->assertNotEmpty($data['errors']); 1158 1159 // Valid data. 1160 $nextmonth = clone($now); 1161 $nextmonth->add(new \DateInterval('P1M')); 1162 $nextmonthtime = $nextmonth->getTimestamp(); 1163 $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nextmonthtime); 1164 $data = \external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data); 1165 $this->assertTrue($data['result']); 1166 $this->assertEmpty($data['errors']); 1167 1168 // Check updated user enrolment. 1169 $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST); 1170 $this->assertEquals(ENROL_USER_ACTIVE, $ue->status); 1171 $this->assertEquals($nowtime, $ue->timestart); 1172 $this->assertEquals($nextmonthtime, $ue->timeend); 1173 1174 // Suspend user. 1175 $data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_SUSPENDED); 1176 $data = \external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data); 1177 $this->assertTrue($data['result']); 1178 $this->assertEmpty($data['errors']); 1179 1180 // Check updated user enrolment. 1181 $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST); 1182 $this->assertEquals(ENROL_USER_SUSPENDED, $ue->status); 1183 } 1184 1185 /** 1186 * dataProvider for test_submit_user_enrolment_form(). 1187 */ 1188 public function submit_user_enrolment_form_provider() { 1189 $now = new \DateTime(); 1190 1191 $nextmonth = clone($now); 1192 $nextmonth->add(new \DateInterval('P1M')); 1193 1194 return [ 1195 'Invalid data' => [ 1196 'customdata' => [ 1197 'status' => ENROL_USER_ACTIVE, 1198 'timestart' => [ 1199 'day' => $now->format('j'), 1200 'month' => $now->format('n'), 1201 'year' => $now->format('Y'), 1202 'hour' => $now->format('G'), 1203 'minute' => 0, 1204 'enabled' => 1, 1205 ], 1206 'timeend' => [ 1207 'day' => $now->format('j'), 1208 'month' => $now->format('n'), 1209 'year' => $now->format('Y'), 1210 'hour' => $now->format('G'), 1211 'minute' => 0, 1212 'enabled' => 1, 1213 ], 1214 ], 1215 'expectedresult' => false, 1216 'validationerror' => true, 1217 ], 1218 'Valid data' => [ 1219 'customdata' => [ 1220 'status' => ENROL_USER_ACTIVE, 1221 'timestart' => [ 1222 'day' => $now->format('j'), 1223 'month' => $now->format('n'), 1224 'year' => $now->format('Y'), 1225 'hour' => $now->format('G'), 1226 'minute' => 0, 1227 'enabled' => 1, 1228 ], 1229 'timeend' => [ 1230 'day' => $nextmonth->format('j'), 1231 'month' => $nextmonth->format('n'), 1232 'year' => $nextmonth->format('Y'), 1233 'hour' => $nextmonth->format('G'), 1234 'minute' => 0, 1235 'enabled' => 1, 1236 ], 1237 ], 1238 'expectedresult' => true, 1239 'validationerror' => false 1240 ], 1241 'Suspend user' => [ 1242 'customdata' => [ 1243 'status' => ENROL_USER_SUSPENDED, 1244 ], 1245 'expectedresult' => true, 1246 'validationerror' => false 1247 ], 1248 ]; 1249 } 1250 1251 /** 1252 * @param array $customdata The data we are providing to the webservice. 1253 * @param bool $expectedresult The result we are expecting to receive from the webservice. 1254 * @param bool $validationerror The validationerror we are expecting to receive from the webservice. 1255 * @dataProvider submit_user_enrolment_form_provider 1256 */ 1257 public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror) { 1258 global $CFG, $DB; 1259 1260 $this->resetAfterTest(true); 1261 $datagen = $this->getDataGenerator(); 1262 1263 /** @var enrol_manual_plugin $manualplugin */ 1264 $manualplugin = enrol_get_plugin('manual'); 1265 1266 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1267 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1268 $course = $datagen->create_course(); 1269 $user = $datagen->create_user(); 1270 $teacher = $datagen->create_user(); 1271 1272 $instanceid = null; 1273 $instances = enrol_get_instances($course->id, true); 1274 foreach ($instances as $inst) { 1275 if ($inst->enrol == 'manual') { 1276 $instanceid = (int)$inst->id; 1277 break; 1278 } 1279 } 1280 if (empty($instanceid)) { 1281 $instanceid = $manualplugin->add_default_instance($course); 1282 if (empty($instanceid)) { 1283 $instanceid = $manualplugin->add_instance($course); 1284 } 1285 } 1286 $this->assertNotNull($instanceid); 1287 1288 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1289 $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1290 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1291 $ueid = (int) $DB->get_field( 1292 'user_enrolments', 1293 'id', 1294 ['enrolid' => $instance->id, 'userid' => $user->id], 1295 MUST_EXIST 1296 ); 1297 1298 // Login as teacher. 1299 $teacher->ignoresesskey = true; 1300 $this->setUser($teacher); 1301 1302 $formdata = [ 1303 'ue' => $ueid, 1304 'ifilter' => 0, 1305 'status' => null, 1306 'timestart' => null, 1307 'duration' => null, 1308 'timeend' => null, 1309 ]; 1310 1311 $formdata = array_merge($formdata, $customdata); 1312 1313 require_once("$CFG->dirroot/enrol/editenrolment_form.php"); 1314 $formdata = enrol_user_enrolment_form::mock_generate_submit_keys($formdata); 1315 1316 $querystring = http_build_query($formdata, '', '&'); 1317 1318 $result = \external_api::clean_returnvalue( 1319 core_enrol_external::submit_user_enrolment_form_returns(), 1320 core_enrol_external::submit_user_enrolment_form($querystring) 1321 ); 1322 1323 $this->assertEqualsCanonicalizing( 1324 ['result' => $expectedresult, 'validationerror' => $validationerror], 1325 $result); 1326 1327 if ($result['result']) { 1328 $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST); 1329 $this->assertEquals($formdata['status'], $ue->status); 1330 } 1331 } 1332 1333 /** 1334 * Test for core_enrol_external::unenrol_user_enrolment(). 1335 */ 1336 public function test_unenerol_user_enrolment() { 1337 global $DB; 1338 1339 $this->resetAfterTest(true); 1340 $datagen = $this->getDataGenerator(); 1341 1342 /** @var enrol_manual_plugin $manualplugin */ 1343 $manualplugin = enrol_get_plugin('manual'); 1344 $this->assertNotNull($manualplugin); 1345 1346 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1347 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1348 $course = $datagen->create_course(); 1349 $user = $datagen->create_user(); 1350 $teacher = $datagen->create_user(); 1351 1352 $instanceid = null; 1353 $instances = enrol_get_instances($course->id, true); 1354 foreach ($instances as $inst) { 1355 if ($inst->enrol == 'manual') { 1356 $instanceid = (int)$inst->id; 1357 break; 1358 } 1359 } 1360 if (empty($instanceid)) { 1361 $instanceid = $manualplugin->add_default_instance($course); 1362 if (empty($instanceid)) { 1363 $instanceid = $manualplugin->add_instance($course); 1364 } 1365 } 1366 $this->assertNotNull($instanceid); 1367 1368 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1369 $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1370 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1371 $ueid = (int)$DB->get_field( 1372 'user_enrolments', 1373 'id', 1374 ['enrolid' => $instance->id, 'userid' => $user->id], 1375 MUST_EXIST 1376 ); 1377 1378 // Login as teacher. 1379 $this->setUser($teacher); 1380 1381 // Invalid data by passing invalid ueid. 1382 $data = core_enrol_external::unenrol_user_enrolment(101010); 1383 $data = \external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data); 1384 $this->assertFalse($data['result']); 1385 $this->assertNotEmpty($data['errors']); 1386 1387 // Valid data. 1388 $data = core_enrol_external::unenrol_user_enrolment($ueid); 1389 $data = \external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data); 1390 $this->assertTrue($data['result']); 1391 $this->assertEmpty($data['errors']); 1392 1393 // Check unenrol user enrolment. 1394 $ue = $DB->count_records('user_enrolments', ['id' => $ueid]); 1395 $this->assertEquals(0, $ue); 1396 } 1397 1398 /** 1399 * Test for core_enrol_external::test_search_users(). 1400 */ 1401 public function test_search_users() { 1402 global $DB; 1403 1404 $this->resetAfterTest(true); 1405 $datagen = $this->getDataGenerator(); 1406 1407 /** @var enrol_manual_plugin $manualplugin */ 1408 $manualplugin = enrol_get_plugin('manual'); 1409 $this->assertNotNull($manualplugin); 1410 1411 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1412 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1413 1414 $course1 = $datagen->create_course(); 1415 $course2 = $datagen->create_course(); 1416 1417 $user1 = $datagen->create_user(['firstname' => 'user 1']); 1418 $user2 = $datagen->create_user(['firstname' => 'user 2']); 1419 $user3 = $datagen->create_user(['firstname' => 'user 3']); 1420 $teacher = $datagen->create_user(['firstname' => 'user 4']); 1421 1422 $instanceid = null; 1423 $instances = enrol_get_instances($course1->id, true); 1424 foreach ($instances as $inst) { 1425 if ($inst->enrol == 'manual') { 1426 $instanceid = (int)$inst->id; 1427 break; 1428 } 1429 } 1430 if (empty($instanceid)) { 1431 $instanceid = $manualplugin->add_default_instance($course1); 1432 if (empty($instanceid)) { 1433 $instanceid = $manualplugin->add_instance($course1); 1434 } 1435 } 1436 $this->assertNotNull($instanceid); 1437 1438 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1439 $manualplugin->enrol_user($instance, $user1->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1440 $manualplugin->enrol_user($instance, $user2->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1441 $manualplugin->enrol_user($instance, $user3->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1442 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1443 1444 $this->setUser($teacher); 1445 1446 // Search for users in a course with enrolled users. 1447 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30); 1448 $this->assertCount(4, $result); 1449 1450 $this->expectException('moodle_exception'); 1451 // Search for users in a course without any enrolled users, shouldn't return anything. 1452 $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30); 1453 $this->assertCount(0, $result); 1454 1455 // Search for invalid first name. 1456 $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30); 1457 $this->assertCount(0, $result); 1458 1459 // Test pagination, it should return only 3 users. 1460 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 3); 1461 $this->assertCount(3, $result); 1462 1463 // Test pagination, it should return only 3 users. 1464 $result = core_enrol_external::search_users($course1->id, 'user 1', true, 0, 1); 1465 $result = $result[0]; 1466 $this->assertEquals($user1->id, $result['id']); 1467 $this->assertEquals($user1->email, $result['email']); 1468 $this->assertEquals(fullname($user1), $result['fullname']); 1469 1470 $this->setUser($user1); 1471 1472 // Search for users in a course with enrolled users. 1473 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30); 1474 $this->assertCount(4, $result); 1475 1476 $this->expectException('moodle_exception'); 1477 // Search for users in a course without any enrolled users, shouldn't return anything. 1478 $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30); 1479 $this->assertCount(0, $result); 1480 1481 // Search for invalid first name. 1482 $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30); 1483 $this->assertCount(0, $result); 1484 } 1485 1486 /** 1487 * Tests the get_potential_users external function (not too much detail because the back-end 1488 * is covered in another test). 1489 */ 1490 public function test_get_potential_users(): void { 1491 $this->resetAfterTest(); 1492 1493 // Create a couple of custom profile fields, one of which is in user identity. 1494 $generator = $this->getDataGenerator(); 1495 $generator->create_custom_profile_field(['datatype' => 'text', 1496 'shortname' => 'researchtopic', 'name' => 'Research topic']); 1497 $generator->create_custom_profile_field(['datatype' => 'text', 1498 'shortname' => 'specialid', 'name' => 'Special id']); 1499 set_config('showuseridentity', 'department,profile_field_specialid'); 1500 1501 // Create a course. 1502 $course = $generator->create_course(); 1503 1504 // Get enrol id for manual enrol plugin. 1505 foreach (enrol_get_instances($course->id, true) as $instance) { 1506 if ($instance->enrol === 'manual') { 1507 $enrolid = $instance->id; 1508 } 1509 } 1510 1511 // Create a couple of test users. 1512 $user1 = $generator->create_user(['firstname' => 'Eigh', 'lastname' => 'User', 1513 'department' => 'Amphibians', 'profile_field_specialid' => 'Q123', 1514 'profile_field_researchtopic' => 'Frogs']); 1515 $user2 = $generator->create_user(['firstname' => 'Anne', 'lastname' => 'Other', 1516 'department' => 'Amphibians', 'profile_field_specialid' => 'Q456', 1517 'profile_field_researchtopic' => 'Toads']); 1518 1519 // Do this as admin user. 1520 $this->setAdminUser(); 1521 1522 // Get potential users and extract the 2 we care about. 1523 $result = core_enrol_external::get_potential_users($course->id, $enrolid, '', false, 0, 10); 1524 $result1 = $this->extract_user_from_result($result, $user1->id); 1525 $result2 = $this->extract_user_from_result($result, $user2->id); 1526 1527 // Check the fields are the expected ones. 1528 $this->assertEquals(['id', 'fullname', 'customfields', 1529 'profileimageurl', 'profileimageurlsmall', 'department'], array_keys($result1)); 1530 $this->assertEquals('Eigh User', $result1['fullname']); 1531 $this->assertEquals('Amphibians', $result1['department']); 1532 1533 // Check the custom fields ONLY include the user identity one. 1534 $fieldvalues = []; 1535 foreach ($result1['customfields'] as $customfield) { 1536 $fieldvalues[$customfield['shortname']] = $customfield['value']; 1537 } 1538 $this->assertEquals(['specialid'], array_keys($fieldvalues)); 1539 $this->AssertEquals('Q123', $fieldvalues['specialid']); 1540 1541 // Just check user 2 is the right user. 1542 $this->assertEquals('Anne Other', $result2['fullname']); 1543 } 1544 1545 /** 1546 * Utility function to get one user out of the get_potential_users result. 1547 * 1548 * @param array $result Result array 1549 * @param int $userid User id 1550 * @return array Data for that user 1551 */ 1552 protected function extract_user_from_result(array $result, int $userid): array { 1553 foreach ($result as $item) { 1554 if ($item['id'] == $userid) { 1555 return $item; 1556 } 1557 } 1558 $this->fail('User not in result: ' . $userid); 1559 } 1560 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body