Search moodle.org's
Developer Documentation

  • 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 37 and 311] [Versions 38 and 311] [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 count of enrolled users
    1127       *
    1128       * @return void
    1129       */
    1130      public function test_count_enrolled_users() {
    1131          global $DB;
    1132  
    1133          $this->resetAfterTest(true);
    1134  
    1135          $course = $this->getDataGenerator()->create_course();
    1136          $context = \context_course::instance($course->id);
    1137  
    1138          $user1 = $this->getDataGenerator()->create_user();
    1139          $user2 = $this->getDataGenerator()->create_user();
    1140  
    1141          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
    1142  
    1143          // Add each user to the manual enrolment instance.
    1144          $manual = enrol_get_plugin('manual');
    1145  
    1146          $manualinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST);
    1147  
    1148          $manual->enrol_user($manualinstance, $user1->id, $studentrole->id);
    1149          $manual->enrol_user($manualinstance, $user2->id, $studentrole->id);
    1150  
    1151          $this->assertEquals(2, count_enrolled_users($context));
    1152  
    1153          // Create a self enrolment instance, enrol first user only.
    1154          $self = enrol_get_plugin('self');
    1155  
    1156          $selfid = $self->add_instance($course,
    1157              ['status' => ENROL_INSTANCE_ENABLED, 'name' => 'Self', 'customint6' => 1, 'roleid' => $studentrole->id]);
    1158          $selfinstance = $DB->get_record('enrol', ['id' => $selfid], '*', MUST_EXIST);
    1159  
    1160          $self->enrol_user($selfinstance, $user1->id, $studentrole->id);
    1161  
    1162          // There are still only two distinct users.
    1163          $this->assertEquals(2, count_enrolled_users($context));
    1164      }
    1165  
    1166      /**
    1167       * Test cases for the test_enrol_get_my_courses_sort_by_last_access test.
    1168       */
    1169      public function get_enrol_get_my_courses_sort_by_last_access_test_cases() {
    1170          $now = time();
    1171  
    1172          $enrolledcoursesdata = [
    1173              ['shortname' => 'a', 'lastaccess' => $now - 2],
    1174              ['shortname' => 'b', 'lastaccess' => $now - 1],
    1175              ['shortname' => 'c', 'lastaccess' => $now],
    1176              ['shortname' => 'd', 'lastaccess' => $now - 1],
    1177              ['shortname' => 'e']
    1178          ];
    1179          $unenrolledcoursesdata = [
    1180              ['shortname' => 'x', 'lastaccess' => $now - 2],
    1181              ['shortname' => 'y', 'lastaccess' => $now - 1],
    1182              ['shortname' => 'z', 'lastaccess' => $now]
    1183          ];
    1184  
    1185          return [
    1186              'empty set' => [
    1187                  'enrolledcoursesdata' => [],
    1188                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1189                  'sort' => 'ul.timeaccess asc',
    1190                  'limit' => 0,
    1191                  'offset' => 0,
    1192                  'expectedcourses' => []
    1193              ],
    1194              'ul.timeaccess asc, shortname asc no limit or offset' => [
    1195                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1196                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1197                  'sort' => 'ul.timeaccess asc, shortname asc',
    1198                  'limit' => 0,
    1199                  'offset' => 0,
    1200                  'expectedcourses' => ['e', 'a', 'b', 'd', 'c']
    1201              ],
    1202              'ul.timeaccess asc, shortname asc with limit no offset' => [
    1203                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1204                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1205                  'sort' => 'ul.timeaccess asc, shortname asc',
    1206                  'limit' => 2,
    1207                  'offset' => 0,
    1208                  'expectedcourses' => ['e', 'a']
    1209              ],
    1210              'ul.timeaccess asc, shortname asc with limit and offset' => [
    1211                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1212                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1213                  'sort' => 'ul.timeaccess asc, shortname asc',
    1214                  'limit' => 2,
    1215                  'offset' => 2,
    1216                  'expectedcourses' => ['b', 'd']
    1217              ],
    1218              'ul.timeaccess asc, shortname asc with limit and offset beyond end of data set' => [
    1219                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1220                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1221                  'sort' => 'ul.timeaccess asc, shortname asc',
    1222                  'limit' => 2,
    1223                  'offset' => 4,
    1224                  'expectedcourses' => ['c']
    1225              ],
    1226              'ul.timeaccess desc, shortname asc no limit or offset' => [
    1227                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1228                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1229                  'sort' => 'ul.timeaccess desc, shortname asc',
    1230                  'limit' => 0,
    1231                  'offset' => 0,
    1232                  'expectedcourses' => ['c', 'b', 'd', 'a', 'e']
    1233              ],
    1234              'ul.timeaccess desc, shortname desc, no limit or offset' => [
    1235                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1236                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1237                  'sort' => 'ul.timeaccess desc, shortname desc',
    1238                  'limit' => 0,
    1239                  'offset' => 0,
    1240                  'expectedcourses' => ['c', 'd', 'b', 'a', 'e']
    1241              ],
    1242              'ul.timeaccess asc, shortname desc, no limit or offset' => [
    1243                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1244                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1245                  'sort' => 'ul.timeaccess asc, shortname desc',
    1246                  'limit' => 0,
    1247                  'offset' => 0,
    1248                  'expectedcourses' => ['e', 'a', 'd', 'b', 'c']
    1249              ],
    1250              'shortname asc, no limit or offset' => [
    1251                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1252                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1253                  'sort' => 'shortname asc',
    1254                  'limit' => 0,
    1255                  'offset' => 0,
    1256                  'expectedcourses' => ['a', 'b', 'c', 'd', 'e']
    1257              ],
    1258              'shortname desc, no limit or offset' => [
    1259                  'enrolledcoursesdata' => $enrolledcoursesdata,
    1260                  'unenrolledcoursesdata' => $unenrolledcoursesdata,
    1261                  'sort' => 'shortname desc',
    1262                  'limit' => 0,
    1263                  'offset' => 0,
    1264                  'expectedcourses' => ['e', 'd', 'c', 'b', 'a']
    1265              ],
    1266          ];
    1267      }
    1268  
    1269      /**
    1270       * Test the get_enrolled_courses_by_timeline_classification function.
    1271       *
    1272       * @dataProvider get_enrol_get_my_courses_sort_by_last_access_test_cases()
    1273       * @param array $enrolledcoursesdata Courses to create and enrol the user in
    1274       * @param array $unenrolledcoursesdata Courses to create nut not enrol the user in
    1275       * @param string $sort Sort string for the enrol function
    1276       * @param int $limit Maximum number of results
    1277       * @param int $offset Offset the courses result set by this amount
    1278       * @param array $expectedcourses Expected courses in result
    1279       */
    1280      public function test_enrol_get_my_courses_sort_by_last_access(
    1281          $enrolledcoursesdata,
    1282          $unenrolledcoursesdata,
    1283          $sort,
    1284          $limit,
    1285          $offset,
    1286          $expectedcourses
    1287      ) {
    1288          global $DB, $CFG;
    1289  
    1290          $this->resetAfterTest();
    1291          $generator = $this->getDataGenerator();
    1292          $student = $generator->create_user();
    1293          $lastaccessrecords = [];
    1294  
    1295          foreach ($enrolledcoursesdata as $coursedata) {
    1296              $lastaccess = null;
    1297  
    1298              if (isset($coursedata['lastaccess'])) {
    1299                  $lastaccess = $coursedata['lastaccess'];
    1300                  unset($coursedata['lastaccess']);
    1301              }
    1302  
    1303              $course = $generator->create_course($coursedata);
    1304              $generator->enrol_user($student->id, $course->id, 'student');
    1305  
    1306              if (!is_null($lastaccess)) {
    1307                  $lastaccessrecords[] = [
    1308                      'userid' => $student->id,
    1309                      'courseid' => $course->id,
    1310                      'timeaccess' => $lastaccess
    1311                  ];
    1312              }
    1313          }
    1314  
    1315          foreach ($unenrolledcoursesdata as $coursedata) {
    1316              $lastaccess = null;
    1317  
    1318              if (isset($coursedata['lastaccess'])) {
    1319                  $lastaccess = $coursedata['lastaccess'];
    1320                  unset($coursedata['lastaccess']);
    1321              }
    1322  
    1323              $course = $generator->create_course($coursedata);
    1324  
    1325              if (!is_null($lastaccess)) {
    1326                  $lastaccessrecords[] = [
    1327                      'userid' => $student->id,
    1328                      'courseid' => $course->id,
    1329                      'timeaccess' => $lastaccess
    1330                  ];
    1331              }
    1332          }
    1333  
    1334          if (!empty($lastaccessrecords)) {
    1335              $DB->insert_records('user_lastaccess', $lastaccessrecords);
    1336          }
    1337  
    1338          $this->setUser($student);
    1339  
    1340          $result = enrol_get_my_courses('shortname', $sort, $limit, [], false, $offset);
    1341          $actual = array_map(function($course) {
    1342              return $course->shortname;
    1343          }, array_values($result));
    1344  
    1345          $this->assertEquals($expectedcourses, $actual);
    1346      }
    1347  
    1348      /**
    1349       * Test enrol_get_course_users_roles function.
    1350       *
    1351       * @return void
    1352       */
    1353      public function test_enrol_get_course_users_roles() {
    1354          global $DB;
    1355  
    1356          $this->resetAfterTest();
    1357  
    1358          $user1 = $this->getDataGenerator()->create_user();
    1359          $user2 = $this->getDataGenerator()->create_user();
    1360          $course = $this->getDataGenerator()->create_course();
    1361          $context = context_course::instance($course->id);
    1362  
    1363          $roles = array();
    1364          $roles['student'] = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST);
    1365          $roles['teacher'] = $DB->get_field('role', 'id', array('shortname' => 'teacher'), MUST_EXIST);
    1366  
    1367          $manual = enrol_get_plugin('manual');
    1368          $this->assertNotEmpty($manual);
    1369  
    1370          $enrol = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
    1371  
    1372          // Test without enrolments.
    1373          $this->assertEmpty(enrol_get_course_users_roles($course->id));
    1374  
    1375          // Test with 1 user, 1 role.
    1376          $manual->enrol_user($enrol, $user1->id, $roles['student']);
    1377          $return = enrol_get_course_users_roles($course->id);
    1378          $this->assertArrayHasKey($user1->id, $return);
    1379          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
    1380          $this->assertArrayNotHasKey($roles['teacher'], $return[$user1->id]);
    1381  
    1382          // Test with 1 user, 2 role.
    1383          $manual->enrol_user($enrol, $user1->id, $roles['teacher']);
    1384          $return = enrol_get_course_users_roles($course->id);
    1385          $this->assertArrayHasKey($user1->id, $return);
    1386          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
    1387          $this->assertArrayHasKey($roles['teacher'], $return[$user1->id]);
    1388  
    1389          // Test with another user, 1 role.
    1390          $manual->enrol_user($enrol, $user2->id, $roles['student']);
    1391          $return = enrol_get_course_users_roles($course->id);
    1392          $this->assertArrayHasKey($user1->id, $return);
    1393          $this->assertArrayHasKey($roles['student'], $return[$user1->id]);
    1394          $this->assertArrayHasKey($roles['teacher'], $return[$user1->id]);
    1395          $this->assertArrayHasKey($user2->id, $return);
    1396          $this->assertArrayHasKey($roles['student'], $return[$user2->id]);
    1397          $this->assertArrayNotHasKey($roles['teacher'], $return[$user2->id]);
    1398      }
    1399  
    1400      /**
    1401       * Test enrol_calculate_duration function
    1402       */
    1403      public function test_enrol_calculate_duration() {
    1404          // Start time 07/01/2019 @ 12:00am (UTC).
    1405          $timestart = 1561939200;
    1406          // End time 07/05/2019 @ 12:00am (UTC).
    1407          $timeend = 1562284800;
    1408          $duration = enrol_calculate_duration($timestart, $timeend);
    1409          $durationinday = $duration / DAYSECS;
    1410          $this->assertEquals(4, $durationinday);
    1411  
    1412          // End time 07/10/2019 @ 12:00am (UTC).
    1413          $timeend = 1562716800;
    1414          $duration = enrol_calculate_duration($timestart, $timeend);
    1415          $durationinday = $duration / DAYSECS;
    1416          $this->assertEquals(9, $durationinday);
    1417      }
    1418  
    1419      /**
    1420       * Test get_enrolled_with_capabilities_join cannotmatchanyrows attribute.
    1421       *
    1422       * @dataProvider get_enrolled_with_capabilities_join_cannotmatchanyrows_data()
    1423       * @param string $capability the tested capability
    1424       * @param bool $useprohibit if the capability must be assigned to prohibit
    1425       * @param int $expectedmatch expected cannotmatchanyrows value
    1426       * @param int $expectedcount expceted count value
    1427       */
    1428      public function test_get_enrolled_with_capabilities_join_cannotmatchanyrows(
    1429          string $capability,
    1430          bool $useprohibit,
    1431          int $expectedmatch,
    1432          int $expectedcount
    1433      ) {
    1434          global $DB, $CFG;
    1435  
    1436          $this->resetAfterTest();
    1437  
    1438          $course = $this->getDataGenerator()->create_course();
    1439          $context = context_course::instance($course->id);
    1440  
    1441          $roleid = $CFG->defaultuserroleid;
    1442  
    1443          // Override capability if necessary.
    1444          if ($useprohibit && $capability) {
    1445              assign_capability($capability, CAP_PROHIBIT, $roleid, $context);
    1446          }
    1447  
    1448          // Check if we must enrol or not.
    1449          $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
    1450  
    1451          $join = get_enrolled_with_capabilities_join($context, '', $capability);
    1452  
    1453          // Execute query.
    1454          $sql = "SELECT COUNT(DISTINCT u.id)
    1455                    FROM {user} u {$join->joins}
    1456                   WHERE {$join->wheres}";
    1457          $countrecords = $DB->count_records_sql($sql, $join->params);
    1458  
    1459          // Validate cannotmatchanyrows.
    1460          $this->assertEquals($expectedmatch, $join->cannotmatchanyrows);
    1461          $this->assertEquals($expectedcount, $countrecords);
    1462      }
    1463  
    1464      /**
    1465       * Data provider for test_get_enrolled_with_capabilities_join_cannotmatchanyrows
    1466       *
    1467       * @return @array of testing scenarios
    1468       */
    1469      public function get_enrolled_with_capabilities_join_cannotmatchanyrows_data() {
    1470          return [
    1471              'no prohibits, no capability' => [
    1472                  'capability' => '',
    1473                  'useprohibit' => false,
    1474                  'expectedmatch' => 0,
    1475                  'expectedcount' => 1,
    1476              ],
    1477              'no prohibits with capability' => [
    1478                  'capability' => 'moodle/course:manageactivities',
    1479                  'useprohibit' => false,
    1480                  'expectedmatch' => 0,
    1481                  'expectedcount' => 1,
    1482              ],
    1483              'prohibits with capability' => [
    1484                  'capability' => 'moodle/course:manageactivities',
    1485                  'useprohibit' => true,
    1486                  'expectedmatch' => 1,
    1487                  'expectedcount' => 0,
    1488              ],
    1489          ];
    1490      }
    1491  }