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