See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 $this->assertEquals($course1->timemodified, $courseenrol['timemodified']); 556 } else { 557 // Check language pack. Should be empty since an incorrect one was used when creating the course. 558 $this->assertEmpty($courseenrol['lang']); 559 $this->assertEquals($course2->fullname, $courseenrol['displayname']); 560 $this->assertEquals([], $courseenrol['overviewfiles']); 561 $this->assertEquals(0, $courseenrol['lastaccess']); 562 $this->assertEquals(0, $courseenrol['progress']); 563 $this->assertEquals(false, $courseenrol['completed']); 564 $this->assertFalse($courseenrol['completionhascriteria']); 565 $this->assertFalse($courseenrol['completionusertracked']); 566 $this->assertFalse($courseenrol['hidden']); 567 $this->assertFalse($courseenrol['isfavourite']); 568 $this->assertEquals(1, $courseenrol['enrolledusercount']); 569 $this->assertEquals($course2->timemodified, $courseenrol['timemodified']); 570 } 571 } 572 573 // Check that returnusercount works correctly. 574 $enrolledincourses = core_enrol_external::get_users_courses($student->id, false); 575 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 576 foreach ($enrolledincourses as $courseenrol) { 577 $this->assertFalse(isset($courseenrol['enrolledusercount'])); 578 } 579 580 // Now check that admin users can see all the info. 581 $this->setAdminUser(); 582 583 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 584 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 585 $this->assertEquals(2, count($enrolledincourses)); 586 foreach ($enrolledincourses as $courseenrol) { 587 if ($courseenrol['id'] == $course1->id) { 588 $this->assertEquals($timenow, $courseenrol['lastaccess']); 589 $this->assertEquals(100.0, $courseenrol['progress']); 590 $this->assertTrue($courseenrol['completionhascriteria']); 591 $this->assertTrue($courseenrol['completionusertracked']); 592 $this->assertFalse($courseenrol['isfavourite']); // This always false. 593 $this->assertFalse($courseenrol['hidden']); // This always false. 594 } else { 595 $this->assertEquals(0, $courseenrol['progress']); 596 $this->assertFalse($courseenrol['completionhascriteria']); 597 $this->assertFalse($courseenrol['completionusertracked']); 598 $this->assertFalse($courseenrol['isfavourite']); // This always false. 599 $this->assertFalse($courseenrol['hidden']); // This always false. 600 } 601 } 602 603 // Check other users can't see private info. 604 $this->setUser($otherstudent); 605 606 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 607 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 608 $this->assertEquals(1, count($enrolledincourses)); 609 610 $this->assertEquals($timenow, $enrolledincourses[0]['lastaccess']); // I can see this, not hidden. 611 $this->assertEquals(null, $enrolledincourses[0]['progress']); // I can't see this, private. 612 613 // Change some global profile visibility fields. 614 $CFG->hiddenuserfields = 'lastaccess'; 615 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 616 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 617 618 $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting. 619 } 620 621 /** 622 * Test that get_users_courses respects the capability to view participants when viewing courses of other user 623 */ 624 public function test_get_users_courses_can_view_participants(): void { 625 global $DB; 626 627 $this->resetAfterTest(); 628 629 $course = $this->getDataGenerator()->create_course(); 630 $context = \context_course::instance($course->id); 631 632 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 633 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 634 635 $this->setUser($user1); 636 637 $courses = core_enrol_external::clean_returnvalue( 638 core_enrol_external::get_users_courses_returns(), 639 core_enrol_external::get_users_courses($user2->id, false) 640 ); 641 642 $this->assertCount(1, $courses); 643 $this->assertEquals($course->id, reset($courses)['id']); 644 645 // Prohibit the capability for viewing course participants. 646 $studentrole = $DB->get_field('role', 'id', ['shortname' => 'student']); 647 assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT, $studentrole, $context->id); 648 649 $courses = core_enrol_external::clean_returnvalue( 650 core_enrol_external::get_users_courses_returns(), 651 core_enrol_external::get_users_courses($user2->id, false) 652 ); 653 $this->assertEmpty($courses); 654 } 655 656 /* 657 * Test that get_users_courses respects the capability to view a users profile when viewing courses of other user 658 */ 659 public function test_get_users_courses_can_view_profile(): void { 660 $this->resetAfterTest(); 661 662 $course = $this->getDataGenerator()->create_course([ 663 'groupmode' => VISIBLEGROUPS, 664 ]); 665 666 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 667 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 668 669 // Create separate groups for each of our students. 670 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 671 groups_add_member($group1, $user1); 672 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 673 groups_add_member($group2, $user2); 674 675 $this->setUser($user1); 676 677 $courses = core_enrol_external::clean_returnvalue( 678 core_enrol_external::get_users_courses_returns(), 679 core_enrol_external::get_users_courses($user2->id, false) 680 ); 681 682 $this->assertCount(1, $courses); 683 $this->assertEquals($course->id, reset($courses)['id']); 684 685 // Change to separate groups mode, so students can't view information about each other in different groups. 686 $course->groupmode = SEPARATEGROUPS; 687 update_course($course); 688 689 $courses = core_enrol_external::clean_returnvalue( 690 core_enrol_external::get_users_courses_returns(), 691 core_enrol_external::get_users_courses($user2->id, false) 692 ); 693 $this->assertEmpty($courses); 694 } 695 696 /** 697 * Test get_users_courses with mathjax in the name. 698 */ 699 public function test_get_users_courses_with_mathjax() { 700 global $DB; 701 702 $this->resetAfterTest(true); 703 704 // Enable MathJax filter in content and headings. 705 $this->configure_filters([ 706 ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], 707 ]); 708 709 // Create a course with MathJax in the name and summary. 710 $coursedata = [ 711 'fullname' => 'Course 1 $$(a+b)=2$$', 712 'shortname' => 'Course 1 $$(a+b)=2$$', 713 'summary' => 'Lightwork Course 1 description $$(a+b)=2$$', 714 'summaryformat' => FORMAT_HTML, 715 ]; 716 717 $course = self::getDataGenerator()->create_course($coursedata); 718 $context = \context_course::instance($course->id); 719 720 // Enrol a student in the course. 721 $student = $this->getDataGenerator()->create_user(); 722 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']); 723 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentroleid); 724 725 $this->setUser($student); 726 727 // Call the external function. 728 $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); 729 730 // We need to execute the return values cleaning process to simulate the web service server. 731 $enrolledincourses = \external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); 732 733 // Check that the amount of courses is the right one. 734 $this->assertCount(1, $enrolledincourses); 735 736 // Filter the values to compare them with the returned ones. 737 $course->fullname = external_format_string($course->fullname, $context->id); 738 $course->shortname = external_format_string($course->shortname, $context->id); 739 list($course->summary, $course->summaryformat) = 740 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0); 741 742 // Compare the values. 743 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['fullname']); 744 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['shortname']); 745 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['summary']); 746 $this->assertEquals($course->fullname, $enrolledincourses[0]['fullname']); 747 $this->assertEquals($course->shortname, $enrolledincourses[0]['shortname']); 748 $this->assertEquals($course->summary, $enrolledincourses[0]['summary']); 749 } 750 751 /** 752 * Test get_course_enrolment_methods 753 */ 754 public function test_get_course_enrolment_methods() { 755 global $DB; 756 757 $this->resetAfterTest(true); 758 759 // Get enrolment plugins. 760 $selfplugin = enrol_get_plugin('self'); 761 $this->assertNotEmpty($selfplugin); 762 $manualplugin = enrol_get_plugin('manual'); 763 $this->assertNotEmpty($manualplugin); 764 765 $studentrole = $DB->get_record('role', array('shortname'=>'student')); 766 $this->assertNotEmpty($studentrole); 767 768 $course1 = self::getDataGenerator()->create_course(); 769 $coursedata = new \stdClass(); 770 $coursedata->visible = 0; 771 $course2 = self::getDataGenerator()->create_course($coursedata); 772 773 // Add enrolment methods for course. 774 $instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED, 775 'name' => 'Test instance 1', 776 'customint6' => 1, 777 'roleid' => $studentrole->id)); 778 $instanceid2 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_DISABLED, 779 'name' => 'Test instance 2', 780 'roleid' => $studentrole->id)); 781 782 $instanceid3 = $manualplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED, 783 'name' => 'Test instance 3')); 784 785 $enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id, 'status' => ENROL_INSTANCE_ENABLED)); 786 $this->assertCount(2, $enrolmentmethods); 787 788 $this->setAdminUser(); 789 790 // Check if information is returned. 791 $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course1->id); 792 $enrolmentmethods = \external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(), 793 $enrolmentmethods); 794 // Enrolment information is currently returned by self enrolment plugin, so count == 1. 795 // This should be changed as we implement get_enrol_info() for other enrolment plugins. 796 $this->assertCount(1, $enrolmentmethods); 797 798 $enrolmentmethod = $enrolmentmethods[0]; 799 $this->assertEquals($course1->id, $enrolmentmethod['courseid']); 800 $this->assertEquals('self', $enrolmentmethod['type']); 801 $this->assertTrue($enrolmentmethod['status']); 802 $this->assertFalse(isset($enrolmentmethod['wsfunction'])); 803 804 $instanceid4 = $selfplugin->add_instance($course2, array('status' => ENROL_INSTANCE_ENABLED, 805 'name' => 'Test instance 4', 806 'roleid' => $studentrole->id, 807 'customint6' => 1, 808 'password' => 'test')); 809 $enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course2->id); 810 $enrolmentmethods = \external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(), 811 $enrolmentmethods); 812 $this->assertCount(1, $enrolmentmethods); 813 814 $enrolmentmethod = $enrolmentmethods[0]; 815 $this->assertEquals($course2->id, $enrolmentmethod['courseid']); 816 $this->assertEquals('self', $enrolmentmethod['type']); 817 $this->assertTrue($enrolmentmethod['status']); 818 $this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']); 819 820 // Try to retrieve information using a normal user for a hidden course. 821 $user = self::getDataGenerator()->create_user(); 822 $this->setUser($user); 823 try { 824 core_enrol_external::get_course_enrolment_methods($course2->id); 825 } catch (\moodle_exception $e) { 826 $this->assertEquals('coursehidden', $e->errorcode); 827 } 828 } 829 830 public function get_enrolled_users_setup($capability) { 831 global $USER; 832 833 $this->resetAfterTest(true); 834 835 $return = new \stdClass(); 836 837 $return->course = self::getDataGenerator()->create_course(); 838 $return->user1 = self::getDataGenerator()->create_user(); 839 $return->user2 = self::getDataGenerator()->create_user(); 840 $return->user3 = self::getDataGenerator()->create_user(); 841 $this->setUser($return->user3); 842 843 // Set the required capabilities by the external function. 844 $return->context = \context_course::instance($return->course->id); 845 $return->roleid = $this->assignUserCapability($capability, $return->context->id); 846 $this->assignUserCapability('moodle/user:viewdetails', $return->context->id, $return->roleid); 847 848 // Enrol the users in the course. 849 $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual'); 850 $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual'); 851 $this->getDataGenerator()->enrol_user($return->user3->id, $return->course->id, $return->roleid, 'manual'); 852 853 return $return; 854 } 855 856 /** 857 * Test get_enrolled_users from core_enrol_external without additional 858 * parameters. 859 */ 860 public function test_get_enrolled_users_without_parameters() { 861 $capability = 'moodle/course:viewparticipants'; 862 $data = $this->get_enrolled_users_setup($capability); 863 864 // Call the external function. 865 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 866 867 // We need to execute the return values cleaning process to simulate the web service server. 868 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 869 870 // Check the result set. 871 $this->assertEquals(3, count($enrolledusers)); 872 $this->assertArrayHasKey('email', $enrolledusers[0]); 873 } 874 875 /** 876 * Test get_enrolled_users from core_enrol_external with some parameters set. 877 */ 878 public function test_get_enrolled_users_with_parameters() { 879 $capability = 'moodle/course:viewparticipants'; 880 $data = $this->get_enrolled_users_setup($capability); 881 882 // Call the function with some parameters set. 883 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id, array( 884 array('name' => 'limitfrom', 'value' => 2), 885 array('name' => 'limitnumber', 'value' => 1), 886 array('name' => 'userfields', 'value' => 'id') 887 )); 888 889 // We need to execute the return values cleaning process to simulate the web service server. 890 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 891 892 // Check the result set, we should only get the 3rd result, which is $user3. 893 $this->assertCount(1, $enrolledusers); 894 $this->assertEquals($data->user3->id, $enrolledusers[0]['id']); 895 $this->assertArrayHasKey('id', $enrolledusers[0]); 896 $this->assertArrayNotHasKey('email', $enrolledusers[0]); 897 } 898 899 900 /** 901 * Test get_enrolled_users last course access. 902 */ 903 public function test_get_enrolled_users_including_lastcourseaccess() { 904 global $DB; 905 $capability = 'moodle/course:viewparticipants'; 906 $data = $this->get_enrolled_users_setup($capability); 907 908 // Call the external function. 909 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 910 // We need to execute the return values cleaning process to simulate the web service server. 911 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 912 913 // Check the result set. 914 $this->assertEquals(3, count($enrolledusers)); 915 $this->assertArrayHasKey('email', $enrolledusers[0]); 916 $this->assertEquals(0, $enrolledusers[0]['lastcourseaccess']); 917 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']); 918 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']); // We forced an access to the course via setUser. 919 920 // Force last access. 921 $timenow = time(); 922 $lastaccess = array( 923 'userid' => $enrolledusers[0]['id'], 924 'courseid' => $data->course->id, 925 'timeaccess' => $timenow 926 ); 927 $DB->insert_record('user_lastaccess', $lastaccess); 928 929 $enrolledusers = core_enrol_external::get_enrolled_users($data->course->id); 930 $enrolledusers = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers); 931 932 // Check the result set. 933 $this->assertEquals(3, count($enrolledusers)); 934 $this->assertEquals($timenow, $enrolledusers[0]['lastcourseaccess']); 935 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']); 936 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']); 937 } 938 939 /** 940 * Test get_enrolled_users from core_enrol_external with capability to 941 * viewparticipants removed. 942 */ 943 public function test_get_enrolled_users_without_capability() { 944 $capability = 'moodle/course:viewparticipants'; 945 $data = $this->get_enrolled_users_setup($capability); 946 947 // Call without required capability. 948 $this->unassignUserCapability($capability, $data->context->id, $data->roleid); 949 $this->expectException(\moodle_exception::class); 950 $categories = core_enrol_external::get_enrolled_users($data->course->id); 951 } 952 953 public function get_enrolled_users_with_capability_setup($capability) { 954 global $USER, $DB; 955 956 $this->resetAfterTest(true); 957 958 $return = new \stdClass(); 959 960 // Create the course and fetch its context. 961 $return->course = self::getDataGenerator()->create_course(); 962 $context = \context_course::instance($return->course->id); 963 964 // Create one teacher, and two students. 965 $return->teacher = self::getDataGenerator()->create_user(); 966 $return->student1 = self::getDataGenerator()->create_user(); 967 $return->student2 = self::getDataGenerator()->create_user(); 968 969 // Create a new student role based on the student archetype but with the capability prohibitted. 970 $fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student'); 971 assign_capability($capability, CAP_PROHIBIT, $fakestudentroleid, $context->id); 972 973 // Enrol all of the users in the course. 974 // * 'teacher' is an editing teacher. 975 // * 'student1' is a standard student. 976 // * 'student2' is a student with the capability prohibitted. 977 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); 978 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); 979 $this->getDataGenerator()->enrol_user($return->teacher->id, $return->course->id, $editingteacherroleid); 980 $this->getDataGenerator()->enrol_user($return->student1->id, $return->course->id, $studentroleid); 981 $this->getDataGenerator()->enrol_user($return->student2->id, $return->course->id, $fakestudentroleid); 982 983 // Log in as the teacher. 984 $this->setUser($return->teacher); 985 986 // Clear caches. 987 accesslib_clear_all_caches_for_unit_testing(); 988 989 return $return; 990 } 991 992 /** 993 * Test get_enrolled_users_with_capability without additional paramaters. 994 */ 995 public function test_get_enrolled_users_with_capability_without_parameters() { 996 $capability = 'moodle/course:viewparticipants'; 997 $data = $this->get_enrolled_users_with_capability_setup($capability); 998 999 $result = core_enrol_external::get_enrolled_users_with_capability( 1000 array( 1001 'coursecapabilities' => array( 1002 'courseid' => $data->course->id, 1003 'capabilities' => array( 1004 $capability, 1005 ), 1006 ), 1007 ), 1008 array() 1009 ); 1010 1011 // We need to execute the return values cleaning process to simulate the web service server. 1012 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1013 1014 // Check an array containing the expected user for the course capability is returned. 1015 $expecteduserlist = $result[0]; 1016 $this->assertEquals($data->course->id, $expecteduserlist['courseid']); 1017 $this->assertEquals($capability, $expecteduserlist['capability']); 1018 $this->assertEquals(2, count($expecteduserlist['users'])); 1019 } 1020 1021 /** 1022 * Test get_enrolled_users_with_capability 1023 */ 1024 public function test_get_enrolled_users_with_capability_with_parameters () { 1025 $capability = 'moodle/course:viewparticipants'; 1026 $data = $this->get_enrolled_users_with_capability_setup($capability); 1027 1028 $result = core_enrol_external::get_enrolled_users_with_capability( 1029 array( 1030 'coursecapabilities' => array( 1031 'courseid' => $data->course->id, 1032 'capabilities' => array( 1033 $capability, 1034 ), 1035 ), 1036 ), 1037 array( 1038 array('name' => 'limitfrom', 'value' => 1), 1039 array('name' => 'limitnumber', 'value' => 1), 1040 array('name' => 'userfields', 'value' => 'id') 1041 ) 1042 ); 1043 1044 // We need to execute the return values cleaning process to simulate the web service server. 1045 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1046 1047 // Check an array containing the expected user for the course capability is returned. 1048 $expecteduserlist = $result[0]['users']; 1049 $expecteduser = reset($expecteduserlist); 1050 $this->assertEquals(1, count($expecteduserlist)); 1051 $this->assertEquals($data->student1->id, $expecteduser['id']); 1052 } 1053 1054 /** 1055 * Test get_enrolled_users last course access. 1056 */ 1057 public function test_get_enrolled_users_with_capability_including_lastcourseaccess() { 1058 global $DB; 1059 $capability = 'moodle/course:viewparticipants'; 1060 $data = $this->get_enrolled_users_with_capability_setup($capability); 1061 1062 $parameters = array( 1063 'coursecapabilities' => array( 1064 'courseid' => $data->course->id, 1065 'capabilities' => array( 1066 $capability, 1067 ), 1068 ), 1069 ); 1070 1071 $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array()); 1072 // We need to execute the return values cleaning process to simulate the web service server. 1073 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1074 1075 // Check an array containing the expected user for the course capability is returned. 1076 $expecteduserlist = $result[0]; 1077 $this->assertEquals($data->course->id, $expecteduserlist['courseid']); 1078 $this->assertEquals($capability, $expecteduserlist['capability']); 1079 $this->assertEquals(2, count($expecteduserlist['users'])); 1080 // We forced an access to the course via setUser. 1081 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']); 1082 $this->assertEquals(0, $expecteduserlist['users'][1]['lastcourseaccess']); 1083 1084 // Force last access. 1085 $timenow = time(); 1086 $lastaccess = array( 1087 'userid' => $expecteduserlist['users'][1]['id'], 1088 'courseid' => $data->course->id, 1089 'timeaccess' => $timenow 1090 ); 1091 $DB->insert_record('user_lastaccess', $lastaccess); 1092 1093 $result = core_enrol_external::get_enrolled_users_with_capability($parameters, array()); 1094 // We need to execute the return values cleaning process to simulate the web service server. 1095 $result = \external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result); 1096 1097 // Check the result set. 1098 $expecteduserlist = $result[0]; 1099 $this->assertEquals(2, count($expecteduserlist['users'])); 1100 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']); 1101 $this->assertEquals($timenow, $expecteduserlist['users'][1]['lastcourseaccess']); 1102 } 1103 1104 /** 1105 * dataProvider for test_submit_user_enrolment_form(). 1106 */ 1107 public function submit_user_enrolment_form_provider() { 1108 $now = new \DateTime(); 1109 1110 $nextmonth = clone($now); 1111 $nextmonth->add(new \DateInterval('P1M')); 1112 1113 return [ 1114 'Invalid data' => [ 1115 'customdata' => [ 1116 'status' => ENROL_USER_ACTIVE, 1117 'timestart' => [ 1118 'day' => $now->format('j'), 1119 'month' => $now->format('n'), 1120 'year' => $now->format('Y'), 1121 'hour' => $now->format('G'), 1122 'minute' => 0, 1123 'enabled' => 1, 1124 ], 1125 'timeend' => [ 1126 'day' => $now->format('j'), 1127 'month' => $now->format('n'), 1128 'year' => $now->format('Y'), 1129 'hour' => $now->format('G'), 1130 'minute' => 0, 1131 'enabled' => 1, 1132 ], 1133 ], 1134 'expectedresult' => false, 1135 'validationerror' => true, 1136 ], 1137 'Valid data' => [ 1138 'customdata' => [ 1139 'status' => ENROL_USER_ACTIVE, 1140 'timestart' => [ 1141 'day' => $now->format('j'), 1142 'month' => $now->format('n'), 1143 'year' => $now->format('Y'), 1144 'hour' => $now->format('G'), 1145 'minute' => 0, 1146 'enabled' => 1, 1147 ], 1148 'timeend' => [ 1149 'day' => $nextmonth->format('j'), 1150 'month' => $nextmonth->format('n'), 1151 'year' => $nextmonth->format('Y'), 1152 'hour' => $nextmonth->format('G'), 1153 'minute' => 0, 1154 'enabled' => 1, 1155 ], 1156 ], 1157 'expectedresult' => true, 1158 'validationerror' => false 1159 ], 1160 'Suspend user' => [ 1161 'customdata' => [ 1162 'status' => ENROL_USER_SUSPENDED, 1163 ], 1164 'expectedresult' => true, 1165 'validationerror' => false 1166 ], 1167 ]; 1168 } 1169 1170 /** 1171 * @param array $customdata The data we are providing to the webservice. 1172 * @param bool $expectedresult The result we are expecting to receive from the webservice. 1173 * @param bool $validationerror The validationerror we are expecting to receive from the webservice. 1174 * @dataProvider submit_user_enrolment_form_provider 1175 */ 1176 public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror) { 1177 global $CFG, $DB; 1178 1179 $this->resetAfterTest(true); 1180 $datagen = $this->getDataGenerator(); 1181 1182 /** @var enrol_manual_plugin $manualplugin */ 1183 $manualplugin = enrol_get_plugin('manual'); 1184 1185 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1186 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1187 $course = $datagen->create_course(); 1188 $user = $datagen->create_user(); 1189 $teacher = $datagen->create_user(); 1190 1191 $instanceid = null; 1192 $instances = enrol_get_instances($course->id, true); 1193 foreach ($instances as $inst) { 1194 if ($inst->enrol == 'manual') { 1195 $instanceid = (int)$inst->id; 1196 break; 1197 } 1198 } 1199 if (empty($instanceid)) { 1200 $instanceid = $manualplugin->add_default_instance($course); 1201 if (empty($instanceid)) { 1202 $instanceid = $manualplugin->add_instance($course); 1203 } 1204 } 1205 $this->assertNotNull($instanceid); 1206 1207 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1208 $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1209 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1210 $ueid = (int) $DB->get_field( 1211 'user_enrolments', 1212 'id', 1213 ['enrolid' => $instance->id, 'userid' => $user->id], 1214 MUST_EXIST 1215 ); 1216 1217 // Login as teacher. 1218 $teacher->ignoresesskey = true; 1219 $this->setUser($teacher); 1220 1221 $formdata = [ 1222 'ue' => $ueid, 1223 'ifilter' => 0, 1224 'status' => null, 1225 'timestart' => null, 1226 'duration' => null, 1227 'timeend' => null, 1228 ]; 1229 1230 $formdata = array_merge($formdata, $customdata); 1231 1232 require_once("$CFG->dirroot/enrol/editenrolment_form.php"); 1233 $formdata = enrol_user_enrolment_form::mock_generate_submit_keys($formdata); 1234 1235 $querystring = http_build_query($formdata, '', '&'); 1236 1237 $result = \external_api::clean_returnvalue( 1238 core_enrol_external::submit_user_enrolment_form_returns(), 1239 core_enrol_external::submit_user_enrolment_form($querystring) 1240 ); 1241 1242 $this->assertEqualsCanonicalizing( 1243 ['result' => $expectedresult, 'validationerror' => $validationerror], 1244 $result); 1245 1246 if ($result['result']) { 1247 $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST); 1248 $this->assertEquals($formdata['status'], $ue->status); 1249 } 1250 } 1251 1252 /** 1253 * Test for core_enrol_external::unenrol_user_enrolment(). 1254 */ 1255 public function test_unenerol_user_enrolment() { 1256 global $DB; 1257 1258 $this->resetAfterTest(true); 1259 $datagen = $this->getDataGenerator(); 1260 1261 /** @var enrol_manual_plugin $manualplugin */ 1262 $manualplugin = enrol_get_plugin('manual'); 1263 $this->assertNotNull($manualplugin); 1264 1265 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1266 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1267 $course = $datagen->create_course(); 1268 $user = $datagen->create_user(); 1269 $teacher = $datagen->create_user(); 1270 1271 $instanceid = null; 1272 $instances = enrol_get_instances($course->id, true); 1273 foreach ($instances as $inst) { 1274 if ($inst->enrol == 'manual') { 1275 $instanceid = (int)$inst->id; 1276 break; 1277 } 1278 } 1279 if (empty($instanceid)) { 1280 $instanceid = $manualplugin->add_default_instance($course); 1281 if (empty($instanceid)) { 1282 $instanceid = $manualplugin->add_instance($course); 1283 } 1284 } 1285 $this->assertNotNull($instanceid); 1286 1287 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1288 $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1289 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1290 $ueid = (int)$DB->get_field( 1291 'user_enrolments', 1292 'id', 1293 ['enrolid' => $instance->id, 'userid' => $user->id], 1294 MUST_EXIST 1295 ); 1296 1297 // Login as teacher. 1298 $this->setUser($teacher); 1299 1300 // Invalid data by passing invalid ueid. 1301 $data = core_enrol_external::unenrol_user_enrolment(101010); 1302 $data = \external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data); 1303 $this->assertFalse($data['result']); 1304 $this->assertNotEmpty($data['errors']); 1305 1306 // Valid data. 1307 $data = core_enrol_external::unenrol_user_enrolment($ueid); 1308 $data = \external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data); 1309 $this->assertTrue($data['result']); 1310 $this->assertEmpty($data['errors']); 1311 1312 // Check unenrol user enrolment. 1313 $ue = $DB->count_records('user_enrolments', ['id' => $ueid]); 1314 $this->assertEquals(0, $ue); 1315 } 1316 1317 /** 1318 * Test for core_enrol_external::test_search_users(). 1319 */ 1320 public function test_search_users() { 1321 global $DB; 1322 1323 $this->resetAfterTest(true); 1324 $datagen = $this->getDataGenerator(); 1325 1326 /** @var enrol_manual_plugin $manualplugin */ 1327 $manualplugin = enrol_get_plugin('manual'); 1328 $this->assertNotNull($manualplugin); 1329 1330 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST); 1331 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST); 1332 1333 $course1 = $datagen->create_course(); 1334 $course2 = $datagen->create_course(); 1335 1336 $user1 = $datagen->create_user(['firstname' => 'user 1']); 1337 $user2 = $datagen->create_user(['firstname' => 'user 2']); 1338 $user3 = $datagen->create_user(['firstname' => 'user 3']); 1339 $teacher = $datagen->create_user(['firstname' => 'user 4']); 1340 1341 $instanceid = null; 1342 $instances = enrol_get_instances($course1->id, true); 1343 foreach ($instances as $inst) { 1344 if ($inst->enrol == 'manual') { 1345 $instanceid = (int)$inst->id; 1346 break; 1347 } 1348 } 1349 if (empty($instanceid)) { 1350 $instanceid = $manualplugin->add_default_instance($course1); 1351 if (empty($instanceid)) { 1352 $instanceid = $manualplugin->add_instance($course1); 1353 } 1354 } 1355 $this->assertNotNull($instanceid); 1356 1357 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST); 1358 $manualplugin->enrol_user($instance, $user1->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1359 $manualplugin->enrol_user($instance, $user2->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1360 $manualplugin->enrol_user($instance, $user3->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE); 1361 $manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE); 1362 1363 $this->setUser($teacher); 1364 1365 // Search for users in a course with enrolled users. 1366 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30); 1367 $this->assertCount(4, $result); 1368 1369 $this->expectException('moodle_exception'); 1370 // Search for users in a course without any enrolled users, shouldn't return anything. 1371 $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30); 1372 $this->assertCount(0, $result); 1373 1374 // Search for invalid first name. 1375 $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30); 1376 $this->assertCount(0, $result); 1377 1378 // Test pagination, it should return only 3 users. 1379 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 3); 1380 $this->assertCount(3, $result); 1381 1382 // Test pagination, it should return only 3 users. 1383 $result = core_enrol_external::search_users($course1->id, 'user 1', true, 0, 1); 1384 $result = $result[0]; 1385 $this->assertEquals($user1->id, $result['id']); 1386 $this->assertEquals($user1->email, $result['email']); 1387 $this->assertEquals(fullname($user1), $result['fullname']); 1388 1389 $this->setUser($user1); 1390 1391 // Search for users in a course with enrolled users. 1392 $result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30); 1393 $this->assertCount(4, $result); 1394 1395 $this->expectException('moodle_exception'); 1396 // Search for users in a course without any enrolled users, shouldn't return anything. 1397 $result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30); 1398 $this->assertCount(0, $result); 1399 1400 // Search for invalid first name. 1401 $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30); 1402 $this->assertCount(0, $result); 1403 } 1404 1405 /** 1406 * Tests the get_potential_users external function (not too much detail because the back-end 1407 * is covered in another test). 1408 */ 1409 public function test_get_potential_users(): void { 1410 $this->resetAfterTest(); 1411 1412 // Create a couple of custom profile fields, one of which is in user identity. 1413 $generator = $this->getDataGenerator(); 1414 $generator->create_custom_profile_field(['datatype' => 'text', 1415 'shortname' => 'researchtopic', 'name' => 'Research topic']); 1416 $generator->create_custom_profile_field(['datatype' => 'text', 1417 'shortname' => 'specialid', 'name' => 'Special id']); 1418 set_config('showuseridentity', 'department,profile_field_specialid'); 1419 1420 // Create a course. 1421 $course = $generator->create_course(); 1422 1423 // Get enrol id for manual enrol plugin. 1424 foreach (enrol_get_instances($course->id, true) as $instance) { 1425 if ($instance->enrol === 'manual') { 1426 $enrolid = $instance->id; 1427 } 1428 } 1429 1430 // Create a couple of test users. 1431 $user1 = $generator->create_user(['firstname' => 'Eigh', 'lastname' => 'User', 1432 'department' => 'Amphibians', 'profile_field_specialid' => 'Q123', 1433 'profile_field_researchtopic' => 'Frogs']); 1434 $user2 = $generator->create_user(['firstname' => 'Anne', 'lastname' => 'Other', 1435 'department' => 'Amphibians', 'profile_field_specialid' => 'Q456', 1436 'profile_field_researchtopic' => 'Toads']); 1437 1438 // Do this as admin user. 1439 $this->setAdminUser(); 1440 1441 // Get potential users and extract the 2 we care about. 1442 $result = core_enrol_external::get_potential_users($course->id, $enrolid, '', false, 0, 10); 1443 $result1 = $this->extract_user_from_result($result, $user1->id); 1444 $result2 = $this->extract_user_from_result($result, $user2->id); 1445 1446 // Check the fields are the expected ones. 1447 $this->assertEquals(['id', 'fullname', 'customfields', 1448 'profileimageurl', 'profileimageurlsmall', 'department'], array_keys($result1)); 1449 $this->assertEquals('Eigh User', $result1['fullname']); 1450 $this->assertEquals('Amphibians', $result1['department']); 1451 1452 // Check the custom fields ONLY include the user identity one. 1453 $fieldvalues = []; 1454 foreach ($result1['customfields'] as $customfield) { 1455 $fieldvalues[$customfield['shortname']] = $customfield['value']; 1456 } 1457 $this->assertEquals(['specialid'], array_keys($fieldvalues)); 1458 $this->AssertEquals('Q123', $fieldvalues['specialid']); 1459 1460 // Just check user 2 is the right user. 1461 $this->assertEquals('Anne Other', $result2['fullname']); 1462 } 1463 1464 /** 1465 * Utility function to get one user out of the get_potential_users result. 1466 * 1467 * @param array $result Result array 1468 * @param int $userid User id 1469 * @return array Data for that user 1470 */ 1471 protected function extract_user_from_result(array $result, int $userid): array { 1472 foreach ($result as $item) { 1473 if ($item['id'] == $userid) { 1474 return $item; 1475 } 1476 } 1477 $this->fail('User not in result: ' . $userid); 1478 } 1479 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body