Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Test non-plugin enrollib parts.
  19   *
  20   * @package    core_enrol
  21   * @category   phpunit
  22   * @copyright  2012 Petr Skoda {@link http://skodak.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  
  29  /**
  30   * Test non-plugin enrollib parts.
  31   *
  32   * @package    core
  33   * @category   phpunit
  34   * @copyright  2012 Petr Skoda {@link http://skodak.org}
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class enrollib_test extends advanced_testcase {
  38  
  39      public function test_enrol_get_all_users_courses() {
  40          global $DB, $CFG;
  41  
  42          $this->resetAfterTest();
  43  
  44          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
  45          $this->assertNotEmpty($studentrole);
  46          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
  47          $this->assertNotEmpty($teacherrole);
  48  
  49          $admin = get_admin();
  50          $user1 = $this->getDataGenerator()->create_user();
  51          $user2 = $this->getDataGenerator()->create_user();
  52          $user3 = $this->getDataGenerator()->create_user();
  53          $user4 = $this->getDataGenerator()->create_user();
  54          $user5 = $this->getDataGenerator()->create_user();
  55  
  56          $category1 = $this->getDataGenerator()->create_category(array('visible'=>0));
  57          $category2 = $this->getDataGenerator()->create_category();
  58  
  59          $course1 = $this->getDataGenerator()->create_course(array(
  60              'shortname' => 'Z',
  61              'idnumber' => '123',
  62              'category' => $category1->id,
  63          ));
  64          $course2 = $this->getDataGenerator()->create_course(array(
  65              'shortname' => 'X',
  66              'idnumber' => '789',
  67              'category' => $category2->id,
  68          ));
  69          $course3 = $this->getDataGenerator()->create_course(array(
  70              'shortname' => 'Y',
  71              'idnumber' => '456',
  72              'category' => $category2->id,
  73              'visible' => 0,
  74          ));
  75          $course4 = $this->getDataGenerator()->create_course(array(
  76              'shortname' => 'W',
  77              'category' => $category2->id,
  78          ));
  79  
  80          $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
  81          $DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$maninstance1->id));
  82          $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
  83          $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
  84          $maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
  85          $maninstance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST);
  86  
  87          $manual = enrol_get_plugin('manual');
  88          $this->assertNotEmpty($manual);
  89  
  90          $manual->enrol_user($maninstance1, $user1->id, $teacherrole->id);
  91          $manual->enrol_user($maninstance1, $user2->id, $studentrole->id);
  92          $manual->enrol_user($maninstance1, $user4->id, $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);
  93          $manual->enrol_user($maninstance1, $admin->id, $studentrole->id);
  94  
  95          $manual->enrol_user($maninstance2, $user1->id);
  96          $manual->enrol_user($maninstance2, $user2->id);
  97          $manual->enrol_user($maninstance2, $user3->id, 0, 1, time()+(60*60));
  98  
  99          $manual->enrol_user($maninstance3, $user1->id);
 100          $manual->enrol_user($maninstance3, $user2->id);
 101          $manual->enrol_user($maninstance3, $user3->id, 0, 1, time()-(60*60));
 102          $manual->enrol_user($maninstance3, $user4->id, 0, 0, 0, ENROL_USER_SUSPENDED);
 103  
 104  
 105          $courses = enrol_get_all_users_courses($CFG->siteguest);
 106          $this->assertSame(array(), $courses);
 107  
 108          $courses = enrol_get_all_users_courses(0);
 109          $this->assertSame(array(), $courses);
 110  
 111          // Results are sorted by visibility, sortorder by default (in our case order of creation)
 112  
 113          $courses = enrol_get_all_users_courses($admin->id);
 114          $this->assertCount(1, $courses);
 115          $this->assertEquals(array($course1->id), array_keys($courses));
 116  
 117          $courses = enrol_get_all_users_courses($admin->id, true);
 118          $this->assertCount(0, $courses);
 119          $this->assertEquals(array(), array_keys($courses));
 120  
 121          $courses = enrol_get_all_users_courses($user1->id);
 122          $this->assertCount(3, $courses);
 123          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 124  
 125          $courses = enrol_get_all_users_courses($user1->id, true);
 126          $this->assertCount(2, $courses);
 127          $this->assertEquals(array($course2->id, $course3->id), array_keys($courses));
 128  
 129          $courses = enrol_get_all_users_courses($user2->id);
 130          $this->assertCount(3, $courses);
 131          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 132  
 133          $courses = enrol_get_all_users_courses($user2->id, true);
 134          $this->assertCount(2, $courses);
 135          $this->assertEquals(array($course2->id, $course3->id), array_keys($courses));
 136  
 137          $courses = enrol_get_all_users_courses($user3->id);
 138          $this->assertCount(2, $courses);
 139          $this->assertEquals(array($course2->id, $course3->id), array_keys($courses));
 140  
 141          $courses = enrol_get_all_users_courses($user3->id, true);
 142          $this->assertCount(1, $courses);
 143          $this->assertEquals(array($course2->id), array_keys($courses));
 144  
 145          $courses = enrol_get_all_users_courses($user4->id);
 146          $this->assertCount(2, $courses);
 147          $this->assertEquals(array($course1->id, $course3->id), array_keys($courses));
 148  
 149          $courses = enrol_get_all_users_courses($user4->id, true);
 150          $this->assertCount(0, $courses);
 151          $this->assertEquals(array(), array_keys($courses));
 152  
 153          // Make sure sorting and columns work.
 154  
 155          $basefields = array('id', 'category', 'sortorder', 'shortname', 'fullname', 'idnumber',
 156              'startdate', 'visible', 'groupmode', 'groupmodeforce', 'defaultgroupingid');
 157  
 158          $courses = enrol_get_all_users_courses($user2->id, true);
 159          $course = reset($courses);
 160          context_helper::preload_from_record($course);
 161          $course = (array)$course;
 162          $this->assertEqualsCanonicalizing($basefields, array_keys($course));
 163  
 164          $courses = enrol_get_all_users_courses($user2->id, false, 'timecreated');
 165          $course = reset($courses);
 166          $this->assertTrue(property_exists($course, 'timecreated'));
 167  
 168          $courses = enrol_get_all_users_courses($user2->id, false, null, 'id DESC');
 169          $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
 170  
 171          // Make sure that implicit sorting defined in navsortmycoursessort is respected.
 172  
 173          $CFG->navsortmycoursessort = 'shortname';
 174  
 175          $courses = enrol_get_all_users_courses($user1->id);
 176          $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
 177  
 178          // But still the explicit sorting takes precedence over the implicit one.
 179  
 180          $courses = enrol_get_all_users_courses($user1->id, false, null, 'shortname DESC');
 181          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 182  
 183          // Make sure that implicit visibility sorting defined in navsortmycourseshiddenlast is respected for all course sortings.
 184  
 185          $CFG->navsortmycoursessort = 'sortorder';
 186          $CFG->navsortmycourseshiddenlast = true;
 187          $courses = enrol_get_all_users_courses($user1->id);
 188          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 189  
 190          $CFG->navsortmycoursessort = 'sortorder';
 191          $CFG->navsortmycourseshiddenlast = false;
 192          $courses = enrol_get_all_users_courses($user1->id);
 193          $this->assertEquals(array($course1->id, $course3->id, $course2->id), array_keys($courses));
 194  
 195          $CFG->navsortmycoursessort = 'fullname';
 196          $CFG->navsortmycourseshiddenlast = true;
 197          $courses = enrol_get_all_users_courses($user1->id);
 198          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 199  
 200          $CFG->navsortmycoursessort = 'fullname';
 201          $CFG->navsortmycourseshiddenlast = false;
 202          $courses = enrol_get_all_users_courses($user1->id);
 203          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 204  
 205          $CFG->navsortmycoursessort = 'shortname';
 206          $CFG->navsortmycourseshiddenlast = true;
 207          $courses = enrol_get_all_users_courses($user1->id);
 208          $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
 209  
 210          $CFG->navsortmycoursessort = 'shortname';
 211          $CFG->navsortmycourseshiddenlast = false;
 212          $courses = enrol_get_all_users_courses($user1->id);
 213          $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
 214  
 215          $CFG->navsortmycoursessort = 'idnumber';
 216          $CFG->navsortmycourseshiddenlast = true;
 217          $courses = enrol_get_all_users_courses($user1->id);
 218          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 219  
 220          $CFG->navsortmycoursessort = 'idnumber';
 221          $CFG->navsortmycourseshiddenlast = false;
 222          $courses = enrol_get_all_users_courses($user1->id);
 223          $this->assertEquals(array($course1->id, $course3->id, $course2->id), array_keys($courses));
 224  
 225          // But still the explicit visibility sorting takes precedence over the implicit one.
 226  
 227          $courses = enrol_get_all_users_courses($user1->id, false, null, 'visible DESC, shortname DESC');
 228          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 229      }
 230  
 231      /**
 232       * Test enrol_course_delete() without passing a user id. When a value for user id is not present, the method
 233       * should delete all enrolment related data in the course.
 234       */
 235      public function test_enrol_course_delete_without_userid() {
 236          global $DB;
 237  
 238          $this->resetAfterTest();
 239  
 240          // Create users.
 241          $user1 = $this->getDataGenerator()->create_user();
 242          $user2 = $this->getDataGenerator()->create_user();
 243          // Create a course.
 244          $course = $this->getDataGenerator()->create_course();
 245          $coursecontext = context_course::instance($course->id);
 246  
 247          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
 248  
 249          $manual = enrol_get_plugin('manual');
 250          $manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST);
 251          // Enrol user1 as a student in the course using manual enrolment.
 252          $manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
 253  
 254          $self = enrol_get_plugin('self');
 255          $selfinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'self'], '*', MUST_EXIST);
 256          $self->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
 257          // Enrol user2 as a student in the course using self enrolment.
 258          $self->enrol_user($selfinstance, $user2->id, $studentrole->id);
 259  
 260          // Delete all enrolment related records in the course.
 261          enrol_course_delete($course);
 262  
 263          // The course enrolment of user1 should not exists.
 264          $user1enrolment = $DB->get_record('user_enrolments',
 265              ['enrolid' => $manualinstance->id, 'userid' => $user1->id]);
 266          $this->assertFalse($user1enrolment);
 267  
 268          // The role assignment of user1 should not exists.
 269          $user1roleassignment = $DB->get_record('role_assignments',
 270              ['roleid' => $studentrole->id, 'userid'=> $user1->id, 'contextid' => $coursecontext->id]
 271          );
 272          $this->assertFalse($user1roleassignment);
 273  
 274          // The course enrolment of user2 should not exists.
 275          $user2enrolment = $DB->get_record('user_enrolments',
 276              ['enrolid' => $selfinstance->id, 'userid' => $user2->id]);
 277          $this->assertFalse($user2enrolment);
 278  
 279          // The role assignment of user2 should not exists.
 280          $user2roleassignment = $DB->get_record('role_assignments',
 281              ['roleid' => $studentrole->id, 'userid'=> $user2->id, 'contextid' => $coursecontext->id]);
 282          $this->assertFalse($user2roleassignment);
 283  
 284          // All existing course enrolment instances should not exists.
 285          $enrolmentinstances = enrol_get_instances($course->id, false);
 286          $this->assertCount(0, $enrolmentinstances);
 287      }
 288  
 289      /**
 290       * Test enrol_course_delete() when user id is present.
 291       * When a value for user id is present, the method should make sure the user has the proper capability to
 292       * un-enrol users before removing the enrolment data. If the capabilities are missing the data should not be removed.
 293       *
 294       * @dataProvider enrol_course_delete_with_userid_provider
 295       * @param array $excludedcapabilities The capabilities that should be excluded from the user's role
 296       * @param bool $expected The expected results
 297       */
 298      public function test_enrol_course_delete_with_userid($excludedcapabilities, $expected) {
 299          global $DB;
 300  
 301          $this->resetAfterTest();
 302          // Create users.
 303          $user1 = $this->getDataGenerator()->create_user();
 304          $user2 = $this->getDataGenerator()->create_user();
 305          $user3 = $this->getDataGenerator()->create_user();
 306          // Create a course.
 307          $course = $this->getDataGenerator()->create_course();
 308          $coursecontext = context_course::instance($course->id);
 309  
 310          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
 311          $editingteacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
 312  
 313          $manual = enrol_get_plugin('manual');
 314          $manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'],
 315              '*', MUST_EXIST);
 316          // Enrol user1 as a student in the course using manual enrolment.
 317          $manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
 318          // Enrol user3 as an editing teacher in the course using manual enrolment.
 319          // By default, the editing teacher role has the capability to un-enroll users which have been enrolled using
 320          // the existing enrolment methods.
 321          $manual->enrol_user($manualinstance, $user3->id, $editingteacherrole->id);
 322  
 323          $self = enrol_get_plugin('self');
 324          $selfinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'self'],
 325              '*', MUST_EXIST);
 326          $self->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
 327          // Enrol user2 as a student in the course using self enrolment.
 328          $self->enrol_user($selfinstance, $user2->id, $studentrole->id);
 329  
 330          foreach($excludedcapabilities as $capability) {
 331              // Un-assign the given capability from the editing teacher role.
 332              unassign_capability($capability, $editingteacherrole->id);
 333          }
 334  
 335          // Delete only enrolment related records in the course where user3 has the required capability.
 336          enrol_course_delete($course, $user3->id);
 337  
 338          // Check the existence of the course enrolment of user1.
 339          $user1enrolmentexists = (bool) $DB->count_records('user_enrolments',
 340              ['enrolid' => $manualinstance->id, 'userid' => $user1->id]);
 341          $this->assertEquals($expected['User 1 course enrolment exists'], $user1enrolmentexists);
 342  
 343          // Check the existence of the role assignment of user1 in the course.
 344          $user1roleassignmentexists = (bool) $DB->count_records('role_assignments',
 345              ['roleid' => $studentrole->id, 'userid' => $user1->id, 'contextid' => $coursecontext->id]);
 346          $this->assertEquals($expected['User 1 role assignment exists'], $user1roleassignmentexists);
 347  
 348          // Check the existence of the course enrolment of user2.
 349          $user2enrolmentexists = (bool) $DB->count_records('user_enrolments',
 350              ['enrolid' => $selfinstance->id, 'userid' => $user2->id]);
 351          $this->assertEquals($expected['User 2 course enrolment exists'], $user2enrolmentexists);
 352  
 353          // Check the existence of the role assignment of user2 in the course.
 354          $user2roleassignmentexists = (bool) $DB->count_records('role_assignments',
 355              ['roleid' => $studentrole->id, 'userid' => $user2->id, 'contextid' => $coursecontext->id]);
 356          $this->assertEquals($expected['User 2 role assignment exists'], $user2roleassignmentexists);
 357  
 358          // Check the existence of the course enrolment of user3.
 359          $user3enrolmentexists = (bool) $DB->count_records('user_enrolments',
 360              ['enrolid' => $manualinstance->id, 'userid' => $user3->id]);
 361          $this->assertEquals($expected['User 3 course enrolment exists'], $user3enrolmentexists);
 362  
 363          // Check the existence of the role assignment of user3 in the course.
 364          $user3roleassignmentexists = (bool) $DB->count_records('role_assignments',
 365              ['roleid' => $editingteacherrole->id, 'userid' => $user3->id, 'contextid' => $coursecontext->id]);
 366          $this->assertEquals($expected['User 3 role assignment exists'], $user3roleassignmentexists);
 367  
 368          // Check the existence of the manual enrolment instance in the course.
 369          $manualinstance = (bool) $DB->count_records('enrol', ['enrol' => 'manual', 'courseid' => $course->id]);
 370          $this->assertEquals($expected['Manual course enrolment instance exists'], $manualinstance);
 371  
 372          // Check existence of the self enrolment instance in the course.
 373          $selfinstance = (bool) $DB->count_records('enrol', ['enrol' => 'self', 'courseid' => $course->id]);
 374          $this->assertEquals($expected['Self course enrolment instance exists'], $selfinstance);
 375      }
 376  
 377      /**
 378       * Data provider for test_enrol_course_delete_with_userid().
 379       *
 380       * @return array
 381       */
 382      public function enrol_course_delete_with_userid_provider() {
 383          return [
 384              'The teacher can un-enrol users in a course' =>
 385                  [
 386                      'excludedcapabilities' => [],
 387                      'results' => [
 388                          // Whether certain enrolment related data still exists in the course after the deletion.
 389                          // When the user has the capabilities to un-enrol users and the enrolment plugins allow manual
 390                          // unenerolment than all course enrolment data should be removed.
 391                          'Manual course enrolment instance exists' => false,
 392                          'Self course enrolment instance exists' => false,
 393                          'User 1 course enrolment exists' => false,
 394                          'User 1 role assignment exists' => false,
 395                          'User 2 course enrolment exists' => false,
 396                          'User 2 role assignment exists' => false,
 397                          'User 3 course enrolment exists' => false,
 398                          'User 3 role assignment exists' => false
 399                      ],
 400                  ],
 401              'The teacher cannot un-enrol self enrolled users'  =>
 402                  [
 403                      'excludedcapabilities' => [
 404                          // Exclude the following capabilities for the editing teacher.
 405                          'enrol/self:unenrol'
 406                      ],
 407                      'results' => [
 408                          // When the user does not have the capabilities to un-enrol self enrolled users, the data
 409                          // related to this enrolment method should not be removed. Everything else should be removed.
 410                          'Manual course enrolment instance exists' => false,
 411                          'Self course enrolment instance exists' => true,
 412                          'User 1 course enrolment exists' => false,
 413                          'User 1 role assignment exists' => false,
 414                          'User 2 course enrolment exists' => true,
 415                          'User 2 role assignment exists' => true,
 416                          'User 3 course enrolment exists' => false,
 417                          'User 3 role assignment exists' => false
 418                      ],
 419                  ],
 420              'The teacher cannot un-enrol self and manually enrolled users' =>
 421                  [
 422                      'excludedcapabilities' => [
 423                          // Exclude the following capabilities for the editing teacher.
 424                          'enrol/manual:unenrol',
 425                          'enrol/self:unenrol'
 426                      ],
 427                      'results' => [
 428                          // When the user does not have the capabilities to un-enrol self and manually enrolled users,
 429                          // the data related to these enrolment methods should not be removed.
 430                          'Manual course enrolment instance exists' => true,
 431                          'Self course enrolment instance exists' => true,
 432                          'User 1 course enrolment exists' => true,
 433                          'User 1 role assignment exists' => true,
 434                          'User 2 course enrolment exists' => true,
 435                          'User 2 role assignment exists' => true,
 436                          'User 3 course enrolment exists' => true,
 437                          'User 3 role assignment exists' => true
 438                      ],
 439                  ],
 440          ];
 441      }
 442  
 443  
 444      public function test_enrol_user_sees_own_courses() {
 445          global $DB, $CFG;
 446  
 447          $this->resetAfterTest();
 448  
 449          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
 450          $this->assertNotEmpty($studentrole);
 451          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
 452          $this->assertNotEmpty($teacherrole);
 453  
 454          $admin = get_admin();
 455          $user1 = $this->getDataGenerator()->create_user();
 456          $user2 = $this->getDataGenerator()->create_user();
 457          $user3 = $this->getDataGenerator()->create_user();
 458          $user4 = $this->getDataGenerator()->create_user();
 459          $user5 = $this->getDataGenerator()->create_user();
 460          $user6 = $this->getDataGenerator()->create_user();
 461  
 462          $category1 = $this->getDataGenerator()->create_category(array('visible'=>0));
 463          $category2 = $this->getDataGenerator()->create_category();
 464          $course1 = $this->getDataGenerator()->create_course(array('category'=>$category1->id));
 465          $course2 = $this->getDataGenerator()->create_course(array('category'=>$category2->id));
 466          $course3 = $this->getDataGenerator()->create_course(array('category'=>$category2->id, 'visible'=>0));
 467          $course4 = $this->getDataGenerator()->create_course(array('category'=>$category2->id));
 468  
 469          $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 470          $DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$maninstance1->id));
 471          $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 472          $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 473          $maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 474          $maninstance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 475  
 476          $manual = enrol_get_plugin('manual');
 477          $this->assertNotEmpty($manual);
 478  
 479          $manual->enrol_user($maninstance1, $admin->id, $studentrole->id);
 480  
 481          $manual->enrol_user($maninstance3, $user1->id, $teacherrole->id);
 482  
 483          $manual->enrol_user($maninstance2, $user2->id, $studentrole->id);
 484  
 485          $manual->enrol_user($maninstance1, $user3->id, $studentrole->id, 1, time()+(60*60));
 486          $manual->enrol_user($maninstance2, $user3->id, 0, 1, time()-(60*60));
 487          $manual->enrol_user($maninstance3, $user2->id, $studentrole->id);
 488          $manual->enrol_user($maninstance4, $user2->id, 0, 0, 0, ENROL_USER_SUSPENDED);
 489  
 490          $manual->enrol_user($maninstance1, $user4->id, $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);
 491          $manual->enrol_user($maninstance3, $user4->id, 0, 0, 0, ENROL_USER_SUSPENDED);
 492  
 493  
 494          $this->assertFalse(enrol_user_sees_own_courses($CFG->siteguest));
 495          $this->assertFalse(enrol_user_sees_own_courses(0));
 496          $this->assertFalse(enrol_user_sees_own_courses($admin));
 497          $this->assertFalse(enrol_user_sees_own_courses(-222)); // Nonexistent user.
 498  
 499          $this->assertTrue(enrol_user_sees_own_courses($user1));
 500          $this->assertTrue(enrol_user_sees_own_courses($user2->id));
 501          $this->assertFalse(enrol_user_sees_own_courses($user3->id));
 502          $this->assertFalse(enrol_user_sees_own_courses($user4));
 503          $this->assertFalse(enrol_user_sees_own_courses($user5));
 504  
 505          $this->setAdminUser();
 506          $this->assertFalse(enrol_user_sees_own_courses());
 507  
 508          $this->setGuestUser();
 509          $this->assertFalse(enrol_user_sees_own_courses());
 510  
 511          $this->setUser(0);
 512          $this->assertFalse(enrol_user_sees_own_courses());
 513  
 514          $this->setUser($user1);
 515          $this->assertTrue(enrol_user_sees_own_courses());
 516  
 517          $this->setUser($user2);
 518          $this->assertTrue(enrol_user_sees_own_courses());
 519  
 520          $this->setUser($user3);
 521          $this->assertFalse(enrol_user_sees_own_courses());
 522  
 523          $this->setUser($user4);
 524          $this->assertFalse(enrol_user_sees_own_courses());
 525  
 526          $this->setUser($user5);
 527          $this->assertFalse(enrol_user_sees_own_courses());
 528  
 529          $user1 = $DB->get_record('user', array('id'=>$user1->id));
 530          $this->setUser($user1);
 531          $reads = $DB->perf_get_reads();
 532          $this->assertTrue(enrol_user_sees_own_courses());
 533          $this->assertGreaterThan($reads, $DB->perf_get_reads());
 534  
 535          $user1 = $DB->get_record('user', array('id'=>$user1->id));
 536          $this->setUser($user1);
 537          require_login($course3);
 538          $reads = $DB->perf_get_reads();
 539          $this->assertTrue(enrol_user_sees_own_courses());
 540          $this->assertEquals($reads, $DB->perf_get_reads());
 541      }
 542  
 543      public function test_enrol_get_shared_courses() {
 544          $this->resetAfterTest();
 545  
 546          $user1 = $this->getDataGenerator()->create_user();
 547          $user2 = $this->getDataGenerator()->create_user();
 548          $user3 = $this->getDataGenerator()->create_user();
 549  
 550          $course1 = $this->getDataGenerator()->create_course();
 551          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
 552          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
 553  
 554          $course2 = $this->getDataGenerator()->create_course();
 555          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
 556  
 557          // Test that user1 and user2 have courses in common.
 558          $this->assertTrue(enrol_get_shared_courses($user1, $user2, false, true));
 559          // Test that user1 and user3 have no courses in common.
 560          $this->assertFalse(enrol_get_shared_courses($user1, $user3, false, true));
 561  
 562          // Test retrieving the courses in common.
 563          $sharedcourses = enrol_get_shared_courses($user1, $user2, true);
 564  
 565          // Only should be one shared course.
 566          $this->assertCount(1, $sharedcourses);
 567          $sharedcourse = array_shift($sharedcourses);
 568          // It should be course 1.
 569          $this->assertEquals($sharedcourse->id, $course1->id);
 570      }
 571  
 572      public function test_enrol_get_shared_courses_different_methods() {
 573          global $DB, $CFG;
 574  
 575          require_once($CFG->dirroot . '/enrol/self/externallib.php');
 576  
 577          $this->resetAfterTest();
 578  
 579          $user1 = $this->getDataGenerator()->create_user();
 580          $user2 = $this->getDataGenerator()->create_user();
 581          $user3 = $this->getDataGenerator()->create_user();
 582  
 583          $course1 = $this->getDataGenerator()->create_course();
 584  
 585          // Enrol user1 and user2 in course1 with a different enrolment methode.
 586          // Add self enrolment method for course1.
 587          $selfplugin = enrol_get_plugin('self');
 588          $this->assertNotEmpty($selfplugin);
 589  
 590          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 591          $this->assertNotEmpty($studentrole);
 592  
 593          $instance1id = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
 594                                                                   'name' => 'Test instance 1',
 595                                                                   'customint6' => 1,
 596                                                                   'roleid' => $studentrole->id));
 597  
 598          $instance1 = $DB->get_record('enrol', array('id' => $instance1id), '*', MUST_EXIST);
 599  
 600          self::setUser($user2);
 601          // Self enrol me (user2).
 602          $result = enrol_self_external::enrol_user($course1->id);
 603  
 604          // Enrol user1 manually.
 605          $this->getDataGenerator()->enrol_user($user1->id, $course1->id, null, 'manual');
 606  
 607          $course2 = $this->getDataGenerator()->create_course();
 608          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
 609  
 610          $course3 = $this->getDataGenerator()->create_course();
 611          $this->getDataGenerator()->enrol_user($user2->id, $course3->id);
 612  
 613          // Test that user1 and user2 have courses in common.
 614          $this->assertTrue(enrol_get_shared_courses($user1, $user2, false, true));
 615          // Test that user1 and user3 have no courses in common.
 616          $this->assertFalse(enrol_get_shared_courses($user1, $user3, false, true));
 617  
 618          // Test retrieving the courses in common.
 619          $sharedcourses = enrol_get_shared_courses($user1, $user2, true);
 620  
 621          // Only should be one shared course.
 622          $this->assertCount(1, $sharedcourses);
 623          $sharedcourse = array_shift($sharedcourses);
 624          // It should be course 1.
 625          $this->assertEquals($sharedcourse->id, $course1->id);
 626      }
 627  
 628      /**
 629       * Test user enrolment created event.
 630       */
 631      public function test_user_enrolment_created_event() {
 632          global $DB;
 633  
 634          $this->resetAfterTest();
 635  
 636          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
 637          $this->assertNotEmpty($studentrole);
 638  
 639          $admin = get_admin();
 640  
 641          $course1 = $this->getDataGenerator()->create_course();
 642  
 643          $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
 644  
 645          $manual = enrol_get_plugin('manual');
 646          $this->assertNotEmpty($manual);
 647  
 648          // Enrol user and capture event.
 649          $sink = $this->redirectEvents();
 650          $manual->enrol_user($maninstance1, $admin->id, $studentrole->id);
 651          $events = $sink->get_events();
 652          $sink->close();
 653          $event = array_shift($events);
 654  
 655          $dbuserenrolled = $DB->get_record('user_enrolments', array('userid' => $admin->id));
 656          $this->assertInstanceOf('\core\event\user_enrolment_created', $event);
 657          $this->assertEquals($dbuserenrolled->id, $event->objectid);
 658          $this->assertEquals(context_course::instance($course1->id), $event->get_context());
 659          $this->assertEquals('user_enrolled', $event->get_legacy_eventname());
 660          $expectedlegacyeventdata = $dbuserenrolled;
 661          $expectedlegacyeventdata->enrol = $manual->get_name();
 662          $expectedlegacyeventdata->courseid = $course1->id;
 663          $this->assertEventLegacyData($expectedlegacyeventdata, $event);
 664          $expected = array($course1->id, 'course', 'enrol', '../enrol/users.php?id=' . $course1->id, $course1->id);
 665          $this->assertEventLegacyLogData($expected, $event);
 666          $this->assertEventContextNotUsed($event);
 667      }
 668  
 669      /**
 670       * Test user_enrolment_deleted event.
 671       */
 672      public function test_user_enrolment_deleted_event() {
 673          global $DB;
 674  
 675          $this->resetAfterTest(true);
 676  
 677          $manualplugin = enrol_get_plugin('manual');
 678          $user = $this->getDataGenerator()->create_user();
 679          $course = $this->getDataGenerator()->create_course();
 680          $student = $DB->get_record('role', array('shortname' => 'student'));
 681  
 682          $enrol = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
 683  
 684          // Enrol user.
 685          $manualplugin->enrol_user($enrol, $user->id, $student->id);
 686  
 687          // Get the user enrolment information, used to validate legacy event data.
 688          $dbuserenrolled = $DB->get_record('user_enrolments', array('userid' => $user->id));
 689  
 690          // Unenrol user and capture event.
 691          $sink = $this->redirectEvents();
 692          $manualplugin->unenrol_user($enrol, $user->id);
 693          $events = $sink->get_events();
 694          $sink->close();
 695          $event = array_pop($events);
 696  
 697          // Validate the event.
 698          $this->assertInstanceOf('\core\event\user_enrolment_deleted', $event);
 699          $this->assertEquals(context_course::instance($course->id), $event->get_context());
 700          $this->assertEquals('user_unenrolled', $event->get_legacy_eventname());
 701          $expectedlegacyeventdata = $dbuserenrolled;
 702          $expectedlegacyeventdata->enrol = $manualplugin->get_name();
 703          $expectedlegacyeventdata->courseid = $course->id;
 704          $expectedlegacyeventdata->lastenrol = true;
 705          $this->assertEventLegacyData($expectedlegacyeventdata, $event);
 706          $expected = array($course->id, 'course', 'unenrol', '../enrol/users.php?id=' . $course->id, $course->id);
 707          $this->assertEventLegacyLogData($expected, $event);
 708          $this->assertEventContextNotUsed($event);
 709      }
 710  
 711      /**
 712       * Test enrol_instance_created, enrol_instance_updated and enrol_instance_deleted events.
 713       */
 714      public function test_instance_events() {
 715          global $DB;
 716  
 717          $this->resetAfterTest(true);
 718  
 719          $selfplugin = enrol_get_plugin('self');
 720          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 721  
 722          $course = $this->getDataGenerator()->create_course();
 723  
 724          // Creating enrol instance.
 725          $sink = $this->redirectEvents();
 726          $instanceid = $selfplugin->add_instance($course, array('status' => ENROL_INSTANCE_ENABLED,
 727                                                                  'name' => 'Test instance 1',
 728                                                                  'customint6' => 1,
 729                                                                  'roleid' => $studentrole->id));
 730          $events = $sink->get_events();
 731          $sink->close();
 732  
 733          $this->assertCount(1, $events);
 734          $event = array_pop($events);
 735          $this->assertInstanceOf('\core\event\enrol_instance_created', $event);
 736          $this->assertEquals(context_course::instance($course->id), $event->get_context());
 737          $this->assertEquals('self', $event->other['enrol']);
 738          $this->assertEventContextNotUsed($event);
 739  
 740          // Updating enrol instance.
 741          $instance = $DB->get_record('enrol', array('id' => $instanceid));
 742          $sink = $this->redirectEvents();
 743          $selfplugin->update_status($instance, ENROL_INSTANCE_DISABLED);
 744  
 745          $events = $sink->get_events();
 746          $sink->close();
 747  
 748          $this->assertCount(1, $events);
 749          $event = array_pop($events);
 750          $this->assertInstanceOf('\core\event\enrol_instance_updated', $event);
 751          $this->assertEquals(context_course::instance($course->id), $event->get_context());
 752          $this->assertEquals('self', $event->other['enrol']);
 753          $this->assertEventContextNotUsed($event);
 754  
 755          // Deleting enrol instance.
 756          $instance = $DB->get_record('enrol', array('id' => $instanceid));
 757          $sink = $this->redirectEvents();
 758          $selfplugin->delete_instance($instance);
 759  
 760          $events = $sink->get_events();
 761          $sink->close();
 762  
 763          $this->assertCount(1, $events);
 764          $event = array_pop($events);
 765          $this->assertInstanceOf('\core\event\enrol_instance_deleted', $event);
 766          $this->assertEquals(context_course::instance($course->id), $event->get_context());
 767          $this->assertEquals('self', $event->other['enrol']);
 768          $this->assertEventContextNotUsed($event);
 769      }
 770  
 771      /**
 772       * Confirms that timemodified field was updated after modification of user enrollment
 773       */
 774      public function test_enrollment_update_timemodified() {
 775          global $DB;
 776  
 777          $this->resetAfterTest(true);
 778          $datagen = $this->getDataGenerator();
 779  
 780          /** @var enrol_manual_plugin $manualplugin */
 781          $manualplugin = enrol_get_plugin('manual');
 782          $this->assertNotNull($manualplugin);
 783  
 784          $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
 785          $course = $datagen->create_course();
 786          $user = $datagen->create_user();
 787  
 788          $instanceid = null;
 789          $instances = enrol_get_instances($course->id, true);
 790          foreach ($instances as $inst) {
 791              if ($inst->enrol == 'manual') {
 792                  $instanceid = (int)$inst->id;
 793                  break;
 794              }
 795          }
 796          if (empty($instanceid)) {
 797              $instanceid = $manualplugin->add_default_instance($course);
 798              if (empty($instanceid)) {
 799                  $instanceid = $manualplugin->add_instance($course);
 800              }
 801          }
 802          $this->assertNotNull($instanceid);
 803  
 804          $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
 805          $manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
 806          $userenrolorig = (int)$DB->get_field(
 807              'user_enrolments',
 808              'timemodified',
 809              ['enrolid' => $instance->id, 'userid' => $user->id],
 810              MUST_EXIST
 811          );
 812          $this->waitForSecond();
 813          $this->waitForSecond();
 814          $manualplugin->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED);
 815          $userenrolpost = (int)$DB->get_field(
 816              'user_enrolments',
 817              'timemodified',
 818              ['enrolid' => $instance->id, 'userid' => $user->id],
 819              MUST_EXIST
 820          );
 821  
 822          $this->assertGreaterThan($userenrolorig, $userenrolpost);
 823      }
 824  
 825      /**
 826       * Test to confirm that enrol_get_my_courses only return the courses that
 827       * the logged in user is enrolled in.
 828       */
 829      public function test_enrol_get_my_courses_only_enrolled_courses() {
 830          $user = $this->getDataGenerator()->create_user();
 831          $course1 = $this->getDataGenerator()->create_course();
 832          $course2 = $this->getDataGenerator()->create_course();
 833          $course3 = $this->getDataGenerator()->create_course();
 834          $course4 = $this->getDataGenerator()->create_course();
 835  
 836          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 837          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 838          $this->getDataGenerator()->enrol_user($user->id, $course3->id);
 839          $this->resetAfterTest(true);
 840          $this->setUser($user);
 841  
 842          // By default this function should return all of the courses the user
 843          // is enrolled in.
 844          $courses = enrol_get_my_courses();
 845  
 846          $this->assertCount(3, $courses);
 847          $this->assertEquals($course1->id, $courses[$course1->id]->id);
 848          $this->assertEquals($course2->id, $courses[$course2->id]->id);
 849          $this->assertEquals($course3->id, $courses[$course3->id]->id);
 850  
 851          // If a set of course ids are provided then the result set will only contain
 852          // these courses.
 853          $courseids = [$course1->id, $course2->id];
 854          $courses = enrol_get_my_courses(['id'], 'visible DESC,sortorder ASC', 0, $courseids);
 855  
 856          $this->assertCount(2, $courses);
 857          $this->assertEquals($course1->id, $courses[$course1->id]->id);
 858          $this->assertEquals($course2->id, $courses[$course2->id]->id);
 859  
 860          // If the course ids list contains any ids for courses the user isn't enrolled in
 861          // then they will be ignored (in this case $course4).
 862          $courseids = [$course1->id, $course2->id, $course4->id];
 863          $courses = enrol_get_my_courses(['id'], 'visible DESC,sortorder ASC', 0, $courseids);
 864  
 865          $this->assertCount(2, $courses);
 866          $this->assertEquals($course1->id, $courses[$course1->id]->id);
 867          $this->assertEquals($course2->id, $courses[$course2->id]->id);
 868      }
 869  
 870      /**
 871       * Tests the enrol_get_my_courses function when using the $includehidden parameter, which
 872       * should remove any courses hidden from the user's timeline
 873       *
 874       * @throws coding_exception
 875       * @throws dml_exception
 876       */
 877      public function test_enrol_get_my_courses_include_hidden() {
 878          global $DB, $CFG;
 879  
 880          $this->resetAfterTest(true);
 881  
 882          // Create test user and 4 courses, two of which have guest access enabled.
 883          $user = $this->getDataGenerator()->create_user();
 884          $course1 = $this->getDataGenerator()->create_course(
 885              (object)array('shortname' => 'X',
 886                  'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
 887                  'enrol_guest_password_0' => ''));
 888          $course2 = $this->getDataGenerator()->create_course(
 889              (object)array('shortname' => 'Z',
 890                  'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
 891                  'enrol_guest_password_0' => ''));
 892          $course3 = $this->getDataGenerator()->create_course(
 893              (object)array('shortname' => 'Y',
 894                  'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
 895                  'enrol_guest_password_0' => 'frog'));
 896          $course4 = $this->getDataGenerator()->create_course(
 897              (object)array('shortname' => 'W',
 898                  'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
 899                  'enrol_guest_password_0' => ''));
 900  
 901          // User is enrolled in first course.
 902          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 903          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 904          $this->getDataGenerator()->enrol_user($user->id, $course3->id);
 905          $this->getDataGenerator()->enrol_user($user->id, $course4->id);
 906  
 907          // Check enrol_get_my_courses basic use (without include hidden provided).
 908          $this->setUser($user);
 909          $courses = enrol_get_my_courses();
 910          $this->assertEquals([$course4->id, $course3->id, $course2->id, $course1->id], array_keys($courses));
 911  
 912          // Hide a course.
 913          set_user_preference('block_myoverview_hidden_course_' . $course3->id, true);
 914  
 915          // Hidden course shouldn't be returned.
 916          $courses = enrol_get_my_courses(null, null, 0, [], false, 0, [$course3->id]);
 917          $this->assertEquals([$course4->id, $course2->id, $course1->id], array_keys($courses));
 918  
 919          // Offset should take into account hidden course.
 920          $courses = enrol_get_my_courses(null, null, 0, [], false, 2, [$course3->id]);
 921          $this->assertEquals([$course1->id], array_keys($courses));
 922      }
 923  
 924      /**
 925       * Tests the enrol_get_my_courses function when using the $allaccessible parameter, which
 926       * includes a wider range of courses (enrolled courses + other accessible ones).
 927       */
 928      public function test_enrol_get_my_courses_all_accessible() {
 929          global $DB, $CFG;
 930  
 931          $this->resetAfterTest(true);
 932  
 933          // Create test user and 4 courses, two of which have guest access enabled.
 934          $user = $this->getDataGenerator()->create_user();
 935          $course1 = $this->getDataGenerator()->create_course(
 936                  (object)array('shortname' => 'X',
 937                  'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
 938                  'enrol_guest_password_0' => ''));
 939          $course2 = $this->getDataGenerator()->create_course(
 940                  (object)array('shortname' => 'Z',
 941                  'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
 942                  'enrol_guest_password_0' => ''));
 943          $course3 = $this->getDataGenerator()->create_course(
 944                  (object)array('shortname' => 'Y',
 945                  'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
 946                  'enrol_guest_password_0' => 'frog'));
 947          $course4 = $this->getDataGenerator()->create_course(
 948                  (object)array('shortname' => 'W',
 949                  'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
 950                  'enrol_guest_password_0' => ''));
 951  
 952          // User is enrolled in first course.
 953          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 954  
 955          // Check enrol_get_my_courses basic use (without all accessible).
 956          $this->setUser($user);
 957          $courses = enrol_get_my_courses();
 958          $this->assertEquals([$course1->id], array_keys($courses));
 959  
 960          // Turn on all accessible, now they can access the second course too.
 961          $courses = enrol_get_my_courses(null, 'id', 0, [], true);
 962          $this->assertEquals([$course1->id, $course2->id], array_keys($courses));
 963  
 964          // Log in as guest to third course.
 965          load_temp_course_role(context_course::instance($course3->id), $CFG->guestroleid);
 966          $courses = enrol_get_my_courses(null, 'id', 0, [], true);
 967          $this->assertEquals([$course1->id, $course2->id, $course3->id], array_keys($courses));
 968  
 969          // Check fields parameter still works. Fields default (certain base fields).
 970          $this->assertObjectHasAttribute('id', $courses[$course3->id]);
 971          $this->assertObjectHasAttribute('shortname', $courses[$course3->id]);
 972          $this->assertObjectNotHasAttribute('summary', $courses[$course3->id]);
 973  
 974          // Specified fields (one, string).
 975          $courses = enrol_get_my_courses('summary', 'id', 0, [], true);
 976          $this->assertObjectHasAttribute('id', $courses[$course3->id]);
 977          $this->assertObjectHasAttribute('shortname', $courses[$course3->id]);
 978          $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
 979          $this->assertObjectNotHasAttribute('summaryformat', $courses[$course3->id]);
 980  
 981          // Specified fields (two, string).
 982          $courses = enrol_get_my_courses('summary, summaryformat', 'id', 0, [], true);
 983          $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
 984          $this->assertObjectHasAttribute('summaryformat', $courses[$course3->id]);
 985  
 986          // Specified fields (two, array).
 987          $courses = enrol_get_my_courses(['summary', 'summaryformat'], 'id', 0, [], true);
 988          $this->assertObjectHasAttribute('summary', $courses[$course3->id]);
 989          $this->assertObjectHasAttribute('summaryformat', $courses[$course3->id]);
 990  
 991          // By default, courses are ordered by sortorder - which by default is most recent first.
 992          $courses = enrol_get_my_courses(null, null, 0, [], true);
 993          $this->assertEquals([$course3->id, $course2->id, $course1->id], array_keys($courses));
 994  
 995          // Make sure that implicit sorting defined in navsortmycoursessort is respected.
 996          $CFG->navsortmycoursessort = 'shortname';
 997          $courses = enrol_get_my_courses(null, null, 0, [], true);
 998          $this->assertEquals([$course1->id, $course3->id, $course2->id], array_keys($courses));
 999  
1000          // But still the explicit sorting takes precedence over the implicit one.
1001          $courses = enrol_get_my_courses(null, 'shortname DESC', 0, [], true);
1002          $this->assertEquals([$course2->id, $course3->id, $course1->id], array_keys($courses));
1003  
1004          // Check filter parameter still works.
1005          $courses = enrol_get_my_courses(null, 'id', 0, [$course2->id, $course3->id, $course4->id], true);
1006          $this->assertEquals([$course2->id, $course3->id], array_keys($courses));
1007  
1008          // Check limit parameter.
1009          $courses = enrol_get_my_courses(null, 'id', 2, [], true);
1010          $this->assertEquals([$course1->id, $course2->id], array_keys($courses));
1011  
1012          // Now try access for a different user who has manager role at system level.
1013          $manager = $this->getDataGenerator()->create_user();
1014          $managerroleid = $DB->get_field('role', 'id', ['shortname' => 'manager']);
1015          role_assign($managerroleid, $manager->id, \context_system::instance()->id);
1016          $this->setUser($manager);
1017  
1018          // With default get enrolled, they don't have any courses.
1019          $courses = enrol_get_my_courses();
1020          $this->assertCount(0, $courses);
1021  
1022          // But with all accessible, they have 4 because they have moodle/course:view everywhere.
1023          $courses = enrol_get_my_courses(null, 'id', 0, [], true);
1024          $this->assertEquals([$course1->id, $course2->id, $course3->id, $course4->id],
1025                  array_keys($courses));
1026  
1027          // If we prohibit manager from course:view on course 1 though...
1028          assign_capability('moodle/course:view', CAP_PROHIBIT, $managerroleid,
1029                  \context_course::instance($course1->id));
1030          $courses = enrol_get_my_courses(null, 'id', 0, [], true);
1031          $this->assertEquals([$course2->id, $course3->id, $course4->id], array_keys($courses));
1032  
1033          // Check for admin user, which has a slightly different query.
1034          $this->setAdminUser();
1035          $courses = enrol_get_my_courses(null, 'id', 0, [], true);
1036          $this->assertEquals([$course1->id, $course2->id, $course3->id, $course4->id], array_keys($courses));
1037      }
1038  
1039      /**
1040       * Data provider for {@see test_enrol_get_my_courses_by_time}
1041       *
1042       * @return array
1043       */
1044      public function enrol_get_my_courses_by_time_provider(): array {
1045          return [
1046              'No start or end time' =>
1047                  [null, null, true],
1048              'Start time now, no end time' =>
1049                  [0, null, true],
1050              'Start time now, end time in the future' =>
1051                  [0, MINSECS, true],
1052              'Start time in the past, no end time' =>
1053                  [-MINSECS, null, true],
1054              'Start time in the past, end time in the future' =>
1055                  [-MINSECS, MINSECS, true],
1056              'Start time in the past, end time in the past' =>
1057                  [-DAYSECS, -HOURSECS, false],
1058              'Start time in the future' =>
1059                  [MINSECS, null, false],
1060          ];
1061      }
1062  
1063      /**
1064       * Test that expected course enrolments are returned when they have timestart / timeend specified
1065       *
1066       * @param int|null $timestartoffset Null for 0, otherwise offset from current time
1067       * @param int|null $timeendoffset Null for 0, otherwise offset from current time
1068       * @param bool $expectreturn
1069       *
1070       * @dataProvider enrol_get_my_courses_by_time_provider
1071       */
1072      public function test_enrol_get_my_courses_by_time(?int $timestartoffset, ?int $timeendoffset, bool $expectreturn): void {
1073          $this->resetAfterTest();
1074  
1075          $time = time();
1076          $timestart = $timestartoffset === null ? 0 : $time + $timestartoffset;
1077          $timeend = $timeendoffset === null ? 0 : $time + $timeendoffset;
1078  
1079          $course = $this->getDataGenerator()->create_course();
1080          $user = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', $timestart, $timeend);
1081          $this->setUser($user);
1082  
1083          $courses = enrol_get_my_courses();
1084          if ($expectreturn) {
1085              $this->assertCount(1, $courses);
1086              $this->assertEquals($course->id, reset($courses)->id);
1087          } else {
1088              $this->assertEmpty($courses);
1089          }
1090      }
1091  
1092      /**
1093       * test_course_users
1094       *
1095       * @return void
1096       */
1097      public function test_course_users() {
1098          $this->resetAfterTest();
1099  
1100          $user1 = $this->getDataGenerator()->create_user();
1101          $user2 = $this->getDataGenerator()->create_user();
1102          $course1 = $this->getDataGenerator()->create_course();
1103          $course2 = $this->getDataGenerator()->create_course();
1104  
1105          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
1106          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
1107          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
1108  
1109          $this->assertCount(2, enrol_get_course_users($course1->id));
1110          $this->assertCount(2, enrol_get_course_users($course1->id, true));
1111  
1112          $this->assertCount(1, enrol_get_course_users($course1->id, true, array($user1->id)));
1113  
1114          $this->assertCount(2, enrol_get_course_users(false, false, array($user1->id)));
1115  
1116          $instances = enrol_get_instances($course1->id, true);
1117          $manualinstance = reset($instances);
1118  
1119          $manualplugin = enrol_get_plugin('manual');
1120          $manualplugin->update_user_enrol($manualinstance, $user1->id, ENROL_USER_SUSPENDED);
1121          $this->assertCount(2, enrol_get_course_users($course1->id, false));
1122          $this->assertCount(1, enrol_get_course_users($course1->id, true));
1123      }
1124  
1125      /**
1126       * test_course_users in groups
1127       *
1128       * @covers \enrol_get_course_users()
1129       * @return void
1130       */
1131      public function test_course_users_in_groups() {
1132          $this->resetAfterTest();
1133  
1134          $user1 = $this->getDataGenerator()->create_user();
1135          $user2 = $this->getDataGenerator()->create_user();
1136          $user3 = $this->getDataGenerator()->create_user();
1137          $course = $this->getDataGenerator()->create_course();
1138          $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1139          $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1140  
1141          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1142          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
1143          $this->getDataGenerator()->enrol_user($user3->id, $course->id);
1144  
1145          $this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $user1->id]);
1146          $this->getDataGenerator()->create_group_member(['groupid' => $group2->id, 'userid' => $user1->id]);
1147          $this->getDataGenerator()->create_group_member(['groupid' => $group2->id, 'userid' => $user2->id]);
1148  
1149          $this->assertCount(3, enrol_get_course_users($course->id));
1150          $this->assertCount(1, enrol_get_course_users($course->id, false, [], [], [$group1->id]));
1151          $this->assertCount(2, enrol_get_course_users($course->id, false, [], [], [$group2->id]));
1152  
1153          $instances = enrol_get_instances($course->id, true);
1154          $manualinstance = reset($instances);
1155  
1156          $manualplugin = enrol_get_plugin('manual');
1157          $manualplugin->update_user_enrol($manualinstance, $user1->id, ENROL_USER_SUSPENDED);
1158          $this->assertCount(2, enrol_get_course_users($course->id, false, [], [], [$group2->id]));
1159          $this->assertCount(1, enrol_get_course_users($course->id, true, [], [], [$group2->id]));
1160      }
1161  
1162      /**
1163       * Test count of enrolled users
1164       *
1165       * @return void
1166       */
1167      public function test_count_enrolled_users() {
1168          global $DB;
1169  
1170          $this->resetAfterTest(true);
1171  
1172          $course = $this->getDataGenerator()->create_course();
1173          $context = \context_course::instance($course->id);
1174  
1175          $user1 = $this->getDataGenerator()->create_user();
1176          $user2 = $this->getDataGenerator()->create_user();
1177  
1178          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
1179  
1180          // Add each user to the manual enrolment instance.
1181          $manual = enrol_get_plugin('manual');
1182  
1183          $manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST);
1184  
1185          $manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
1186          $manual->enrol_user($manualinstance, $user2->id, $studentrole->id);
1187  
1188          $this->assertEquals(2, count_enrolled_users($context));
1189  
1190          // Create a self enrolment instance, enrol first user only.
1191          $self = enrol_get_plugin('self');
1192  
1193          $selfid = $self->add_instance($course,
1194              ['status' => ENROL_INSTANCE_ENABLED, 'name' => 'Self', 'customint6' => 1, 'roleid' => $studentrole->id]);
1195          $selfinstance = $DB->get_record('enrol', ['id' => $selfid], '*', MUST_EXIST);
1196  
1197          $self->enrol_user($selfinstance, $user1->id, $studentrole->id);
1198  
1199          // There are still only two distinct users.
1200          $this->assertEquals(2, count_enrolled_users($context));
1201      }
1202  
1203      /**
1204       * Test cases for the test_enrol_get_my_courses_sort_by_last_access test.
1205       */
1206      public function get_enrol_get_my_courses_sort_by_last_access_test_cases() {
1207          $now = time();
1208  
1209          $enrolledcoursesdata = [
1210              ['shortname' => 'a', 'lastaccess' => $now - 2],
1211              ['shortname' => 'b', 'lastaccess' => $now - 1],
1212              ['shortname' => 'c', 'lastaccess' => $now],
1213              ['shortname' => 'd', 'lastaccess' => $now - 1],
1214              ['shortname' => 'e']
1215          ];
1216          $unenrolledcoursesdata = [
1217              ['shortname' => 'x', 'lastaccess' => $now - 2],
1218              ['shortname' => 'y', 'lastaccess' => $now - 1],
1219              ['shortname' => 'z', 'lastaccess' => $now]
1220          ];
1221  
1222          return [
1223              'empty set' => [
1224                  'enrolledcoursesdata' => [],
1225                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1226                  'sort' => 'ul.timeaccess asc',
1227                  'limit' => 0,
1228                  'offset' => 0,
1229                  'expectedcourses' => []
1230              ],
1231              'ul.timeaccess asc, shortname asc no limit or offset' => [
1232                  'enrolledcoursesdata' => $enrolledcoursesdata,
1233                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1234                  'sort' => 'ul.timeaccess asc, shortname asc',
1235                  'limit' => 0,
1236                  'offset' => 0,
1237                  'expectedcourses' => ['e', 'a', 'b', 'd', 'c']
1238              ],
1239              'ul.timeaccess asc, shortname asc with limit no offset' => [
1240                  'enrolledcoursesdata' => $enrolledcoursesdata,
1241                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1242                  'sort' => 'ul.timeaccess asc, shortname asc',
1243                  'limit' => 2,
1244                  'offset' => 0,
1245                  'expectedcourses' => ['e', 'a']
1246              ],
1247              'ul.timeaccess asc, shortname asc with limit and offset' => [
1248                  'enrolledcoursesdata' => $enrolledcoursesdata,
1249                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1250                  'sort' => 'ul.timeaccess asc, shortname asc',
1251                  'limit' => 2,
1252                  'offset' => 2,
1253                  'expectedcourses' => ['b', 'd']
1254              ],
1255              'ul.timeaccess asc, shortname asc with limit and offset beyond end of data set' => [
1256                  'enrolledcoursesdata' => $enrolledcoursesdata,
1257                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1258                  'sort' => 'ul.timeaccess asc, shortname asc',
1259                  'limit' => 2,
1260                  'offset' => 4,
1261                  'expectedcourses' => ['c']
1262              ],
1263              'ul.timeaccess desc, shortname asc no limit or offset' => [
1264                  'enrolledcoursesdata' => $enrolledcoursesdata,
1265                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1266                  'sort' => 'ul.timeaccess desc, shortname asc',
1267                  'limit' => 0,
1268                  'offset' => 0,
1269                  'expectedcourses' => ['c', 'b', 'd', 'a', 'e']
1270              ],
1271              'ul.timeaccess desc, shortname desc, no limit or offset' => [
1272                  'enrolledcoursesdata' => $enrolledcoursesdata,
1273                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1274                  'sort' => 'ul.timeaccess desc, shortname desc',
1275                  'limit' => 0,
1276                  'offset' => 0,
1277                  'expectedcourses' => ['c', 'd', 'b', 'a', 'e']
1278              ],
1279              'ul.timeaccess asc, shortname desc, no limit or offset' => [
1280                  'enrolledcoursesdata' => $enrolledcoursesdata,
1281                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1282                  'sort' => 'ul.timeaccess asc, shortname desc',
1283                  'limit' => 0,
1284                  'offset' => 0,
1285                  'expectedcourses' => ['e', 'a', 'd', 'b', 'c']
1286              ],
1287              'shortname asc, no limit or offset' => [
1288                  'enrolledcoursesdata' => $enrolledcoursesdata,
1289                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1290                  'sort' => 'shortname asc',
1291                  'limit' => 0,
1292                  'offset' => 0,
1293                  'expectedcourses' => ['a', 'b', 'c', 'd', 'e']
1294              ],
1295              'shortname desc, no limit or offset' => [
1296                  'enrolledcoursesdata' => $enrolledcoursesdata,
1297                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
1298                  'sort' => 'shortname desc',
1299                  'limit' => 0,
1300                  'offset' => 0,
1301                  'expectedcourses' => ['e', 'd', 'c', 'b', 'a']
1302              ],
1303          ];
1304      }
1305  
1306      /**
1307       * Test the get_enrolled_courses_by_timeline_classification function.
1308       *
1309       * @dataProvider get_enrol_get_my_courses_sort_by_last_access_test_cases()
1310       * @param array $enrolledcoursesdata Courses to create and enrol the user in
1311       * @param array $unenrolledcoursesdata Courses to create nut not enrol the user in
1312       * @param string $sort Sort string for the enrol function
1313       * @param int $limit Maximum number of results
1314       * @param int $offset Offset the courses result set by this amount
1315       * @param array $expectedcourses Expected courses in result
1316       */
1317      public function test_enrol_get_my_courses_sort_by_last_access(
1318          $enrolledcoursesdata,
1319          $unenrolledcoursesdata,
1320          $sort,
1321          $limit,
1322          $offset,
1323          $expectedcourses
1324      ) {
1325          global $DB, $CFG;
1326  
1327          $this->resetAfterTest();
1328          $generator = $this->getDataGenerator();
1329          $student = $generator->create_user();
1330          $lastaccessrecords = [];
1331  
1332          foreach ($enrolledcoursesdata as $coursedata) {
1333              $lastaccess = null;
1334  
1335              if (isset($coursedata['lastaccess'])) {
1336                  $lastaccess = $coursedata['lastaccess'];
1337                  unset($coursedata['lastaccess']);
1338              }
1339  
1340              $course = $generator->create_course($coursedata);
1341              $generator->enrol_user($student->id, $course->id, 'student');
1342  
1343              if (!is_null($lastaccess)) {
1344                  $lastaccessrecords[] = [
1345                      'userid' => $student->id,
1346                      'courseid' => $course->id,
1347                      'timeaccess' => $lastaccess
1348                  ];
1349              }
1350          }
1351  
1352          foreach ($unenrolledcoursesdata as $coursedata) {
1353              $lastaccess = null;
1354  
1355              if (isset($coursedata['lastaccess'])) {
1356                  $lastaccess = $coursedata['lastaccess'];
1357                  unset($coursedata['lastaccess']);
1358              }
1359  
1360              $course = $generator->create_course($coursedata);
1361  
1362              if (!is_null($lastaccess)) {
1363                  $lastaccessrecords[] = [
1364                      'userid' => $student->id,
1365                      'courseid' => $course->id,
1366                      'timeaccess' => $lastaccess
1367                  ];
1368              }
1369          }
1370  
1371          if (!empty($lastaccessrecords)) {
1372              $DB->insert_records('user_lastaccess', $lastaccessrecords);
1373          }
1374  
1375          $this->setUser($student);
1376  
1377          $result = enrol_get_my_courses('shortname', $sort, $limit, [], false, $offset);
1378          $actual = array_map(function($course) {
1379              return $course->shortname;
1380          }, array_values($result));
1381  
1382          $this->assertEquals($expectedcourses, $actual);
1383      }
1384  
1385      /**
1386       * Test enrol_get_course_users_roles function.
1387       *
1388       * @return void
1389       */
1390      public function test_enrol_get_course_users_roles() {
1391          global $DB;
1392  
1393          $this->resetAfterTest();
1394  
1395          $user1 = $this->getDataGenerator()->create_user();
1396          $user2 = $this->getDataGenerator()->create_user();
1397          $course = $this->getDataGenerator()->create_course();
1398          $context = context_course::instance($course->id);
1399  
1400          $roles = array();
1401          $roles['student'] = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST);
1402          $roles['teacher'] = $DB->get_field('role', 'id', array('shortname' => 'teacher'), MUST_EXIST);
1403  
1404          $manual = enrol_get_plugin('manual');
1405          $this->assertNotEmpty($manual);
1406  
1407          $enrol = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
1408  
1409          // Test without enrolments.
1410          $this->assertEmpty(enrol_get_course_users_roles($course->id));
1411  
1412          // Test with 1 user, 1 role.
1413          $manual->enrol_user($enrol, $user1->id, $roles['student']);
1414          $return = enrol_get_course_users_roles($course->id);
1415          $this->assertArrayHasKey($user1->id, $return);
1416          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
1417          $this->assertArrayNotHasKey($roles['teacher'], $return[$user1->id]);
1418  
1419          // Test with 1 user, 2 role.
1420          $manual->enrol_user($enrol, $user1->id, $roles['teacher']);
1421          $return = enrol_get_course_users_roles($course->id);
1422          $this->assertArrayHasKey($user1->id, $return);
1423          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
1424          $this->assertArrayHasKey($roles['teacher'], $return[$user1->id]);
1425  
1426          // Test with another user, 1 role.
1427          $manual->enrol_user($enrol, $user2->id, $roles['student']);
1428          $return = enrol_get_course_users_roles($course->id);
1429          $this->assertArrayHasKey($user1->id, $return);
1430          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
1431          $this->assertArrayHasKey($roles['teacher'], $return[$user1->id]);
1432          $this->assertArrayHasKey($user2->id, $return);
1433          $this->assertArrayHasKey($roles['student'], $return[$user2->id]);
1434          $this->assertArrayNotHasKey($roles['teacher'], $return[$user2->id]);
1435      }
1436  
1437      /**
1438       * Test enrol_calculate_duration function
1439       */
1440      public function test_enrol_calculate_duration() {
1441          // Start time 07/01/2019 @ 12:00am (UTC).
1442          $timestart = 1561939200;
1443          // End time 07/05/2019 @ 12:00am (UTC).
1444          $timeend = 1562284800;
1445          $duration = enrol_calculate_duration($timestart, $timeend);
1446          $durationinday = $duration / DAYSECS;
1447          $this->assertEquals(4, $durationinday);
1448  
1449          // End time 07/10/2019 @ 12:00am (UTC).
1450          $timeend = 1562716800;
1451          $duration = enrol_calculate_duration($timestart, $timeend);
1452          $durationinday = $duration / DAYSECS;
1453          $this->assertEquals(9, $durationinday);
1454      }
1455  
1456      /**
1457       * Test get_enrolled_with_capabilities_join cannotmatchanyrows attribute.
1458       *
1459       * @dataProvider get_enrolled_with_capabilities_join_cannotmatchanyrows_data()
1460       * @param string $capability the tested capability
1461       * @param bool $useprohibit if the capability must be assigned to prohibit
1462       * @param int $expectedmatch expected cannotmatchanyrows value
1463       * @param int $expectedcount expceted count value
1464       */
1465      public function test_get_enrolled_with_capabilities_join_cannotmatchanyrows(
1466          string $capability,
1467          bool $useprohibit,
1468          int $expectedmatch,
1469          int $expectedcount
1470      ) {
1471          global $DB, $CFG;
1472  
1473          $this->resetAfterTest();
1474  
1475          $course = $this->getDataGenerator()->create_course();
1476          $context = context_course::instance($course->id);
1477  
1478          $roleid = $CFG->defaultuserroleid;
1479  
1480          // Override capability if necessary.
1481          if ($useprohibit && $capability) {
1482              assign_capability($capability, CAP_PROHIBIT, $roleid, $context);
1483          }
1484  
1485          // Check if we must enrol or not.
1486          $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1487  
1488          $join = get_enrolled_with_capabilities_join($context, '', $capability);
1489  
1490          // Execute query.
1491          $sql = "SELECT COUNT(DISTINCT u.id)
1492                    FROM {user} u {$join->joins}
1493                   WHERE {$join->wheres}";
1494          $countrecords = $DB->count_records_sql($sql, $join->params);
1495  
1496          // Validate cannotmatchanyrows.
1497          $this->assertEquals($expectedmatch, $join->cannotmatchanyrows);
1498          $this->assertEquals($expectedcount, $countrecords);
1499      }
1500  
1501      /**
1502       * Data provider for test_get_enrolled_with_capabilities_join_cannotmatchanyrows
1503       *
1504       * @return @array of testing scenarios
1505       */
1506      public function get_enrolled_with_capabilities_join_cannotmatchanyrows_data() {
1507          return [
1508              'no prohibits, no capability' => [
1509                  'capability' => '',
1510                  'useprohibit' => false,
1511                  'expectedmatch' => 0,
1512                  'expectedcount' => 1,
1513              ],
1514              'no prohibits with capability' => [
1515                  'capability' => 'moodle/course:manageactivities',
1516                  'useprohibit' => false,
1517                  'expectedmatch' => 0,
1518                  'expectedcount' => 1,
1519              ],
1520              'prohibits with capability' => [
1521                  'capability' => 'moodle/course:manageactivities',
1522                  'useprohibit' => true,
1523                  'expectedmatch' => 1,
1524                  'expectedcount' => 0,
1525              ],
1526          ];
1527      }
1528  }