Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.7.x will end 11 May 2020 (12 months).
  • Bug fixes for security issues in 3.7.x will end 9 November 2020 (18 months) - Support has ended.
  • minimum PHP 7.1.0 Note: minimum PHP version has increased since Moodle 3.6. PHP 7.2.x and 7.3.x are supported too. PHP 7.x could have some engine limitations.
  • Differences Between: [Versions 35 and 37] [Versions 36 and 37] [Versions 37 and 310] [Versions 37 and 311] [Versions 37 and 38] [Versions 37 and 39]

       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   * External calendar functions unit tests
      19   *
      20   * @package    core_calendar
      21   * @category   external
      22   * @copyright  2012 Ankit Agarwal
      23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      24   */
      25  
      26  defined('MOODLE_INTERNAL') || die();
      27  
      28  global $CFG;
      29  
      30  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
      31  
      32  /**
      33   * External course functions unit tests
      34   *
      35   * @package    core_calendar
      36   * @category   external
      37   * @copyright  2012 Ankit Agarwal
      38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      39   * @since Moodle 2.5
      40   */
      41  class core_calendar_externallib_testcase extends externallib_advanced_testcase {
      42  
      43      /**
      44       * Tests set up
      45       */
      46      protected function setUp() {
      47          global $CFG;
      48          require_once($CFG->dirroot . '/calendar/externallib.php');
      49      }
      50  
      51      /** Create calendar events or update them
      52       * Set $prop->id, if you want to do an update instead of creating an new event
      53       *
      54       * @param string $name        Event title
      55       * @param int    $userid      User id
      56       * @param string $type        Event type
      57       * @param int    $repeats     Number of repeated events to create
      58       * @param int    $timestart   Time stamp of the event start
      59       * @param mixed  $prop        List of event properties as array or object
      60       * @return mixed              Event object or false;
      61       * @since Moodle 2.5
      62       */
      63  
      64      public static function create_calendar_event($name, $userid = 0, $type = 'user', $repeats = 0, $timestart  = null, $prop = null) {
      65          global $CFG, $DB, $SITE;
      66  
      67          require_once("$CFG->dirroot/calendar/lib.php");
      68          if (!empty($prop)) {
      69              if (is_array($prop)) {
      70                  $prop = (object)$prop;
      71              }
      72          } else {
      73              $prop = new stdClass();
      74          }
      75          $prop->name = $name;
      76          if (empty($prop->eventtype)) {
      77              $prop->eventtype = $type;
      78          }
      79          if (empty($prop->repeats)) {
      80              $prop->repeats = $repeats;
      81          }
      82          if (empty($prop->timestart)) {
      83              $prop->timestart = time();
      84          }
      85          if (empty($prop->timeduration)) {
      86              $prop->timeduration = 0;
      87          }
      88          if (empty($prop->timesort)) {
      89              $prop->timesort = 0;
      90          }
      91          if (empty($prop->type)) {
      92              $prop->type = CALENDAR_EVENT_TYPE_STANDARD;
      93          }
      94          if (empty($prop->repeats)) {
      95              $prop->repeat = 0;
      96          } else {
      97              $prop->repeat = 1;
      98          }
      99          if (empty($prop->userid)) {
     100              if (!empty($userid)) {
     101                  $prop->userid = $userid;
     102              } else {
     103                  $prop->userid = 0;
     104              }
     105          }
     106          if (!isset($prop->courseid)) {
     107              $prop->courseid = $SITE->id;
     108          }
     109  
     110          // Determine event priority.
     111          if ($prop->courseid == 0 && isset($prop->groupid) && $prop->groupid == 0 && !empty($prop->userid)) {
     112              // User override event.
     113              $prop->priority = CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
     114          } else if ($prop->courseid != $SITE->id && !empty($prop->groupid)) {
     115              // Group override event.
     116              $priorityparams = ['courseid' => $prop->courseid, 'groupid' => $prop->groupid];
     117              // Group override event with the highest priority.
     118              $groupevents = $DB->get_records('event', $priorityparams, 'priority DESC', 'id, priority', 0, 1);
     119              $priority = 1;
     120              if (!empty($groupevents)) {
     121                  $event = reset($groupevents);
     122                  if (!empty($event->priority)) {
     123                      $priority = $event->priority + 1;
     124                  }
     125              }
     126              $prop->priority = $priority;
     127          }
     128  
     129          $event = new calendar_event($prop);
     130          return $event->create($prop);
     131      }
     132  
     133      public function test_create_calendar_events () {
     134          global $DB, $USER;
     135  
     136          $this->setAdminUser();
     137          $this->resetAfterTest();
     138          $prevcount = count($DB->get_records("event"));
     139  
     140          // Create a few events and do asserts.
     141          $this->create_calendar_event('test', $USER->id);
     142          $where = $DB->sql_compare_text('name') ." = ?";
     143          $count = count($DB->get_records_select("event", $where, array('test')));
     144          $this->assertEquals(1, $count);
     145          $aftercount = count($DB->get_records("event"));
     146          $this->assertEquals($prevcount + 1, $aftercount);
     147  
     148          $this->create_calendar_event('user', $USER->id, 'user', 3);
     149          $where = $DB->sql_compare_text('name') ." = ?";
     150          $count = count($DB->get_records_select("event", $where, array('user')));
     151  
     152          $this->assertEquals(3, $count);
     153          $aftercount = count($DB->get_records("event"));
     154          $this->assertEquals($prevcount + 4, $aftercount);
     155  
     156      }
     157  
     158      /**
     159       * Test delete_calendar_events
     160       *
     161       * @expectedException moodle_exception
     162       */
     163      public function test_delete_calendar_events() {
     164          global $DB, $USER;
     165  
     166          $this->resetAfterTest(true);
     167          $this->setAdminUser();
     168  
     169          // Create a few stuff to test with.
     170          $user = $this->getDataGenerator()->create_user();
     171          $course = $this->getDataGenerator()->create_course();
     172          $record = new stdClass();
     173          $record->courseid = $course->id;
     174          $group = $this->getDataGenerator()->create_group($record);
     175  
     176          $notdeletedcount = $DB->count_records('event');
     177  
     178          // Let's create a few events.
     179          $siteevent = $this->create_calendar_event('site', $USER->id, 'site');
     180          $record = new stdClass();
     181          $record->courseid = $course->id;
     182          $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 2, time(), $record);
     183          $userevent = $this->create_calendar_event('user', $USER->id);
     184          $record = new stdClass();
     185          $record->courseid = $course->id;
     186          $record->groupid = $group->id;
     187          $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record);
     188  
     189          // Now lets try to delete stuff with proper rights.
     190          $events = array(
     191                  array('eventid' => $siteevent->id, 'repeat' => 0),
     192                  array('eventid' => $courseevent->id, 'repeat' => 1),
     193                  array('eventid' => $userevent->id, 'repeat' => 0),
     194                  array('eventid' => $groupevent->id, 'repeat' => 0)
     195                  );
     196          core_calendar_external::delete_calendar_events($events);
     197  
     198          // Check to see if things were deleted properly.
     199          $deletedcount = $DB->count_records('event');
     200          $this->assertEquals($notdeletedcount, $deletedcount);
     201  
     202          // Let's create a few events.
     203          $siteevent = $this->create_calendar_event('site', $USER->id, 'site');
     204          $record = new stdClass();
     205          $record->courseid = $course->id;
     206          $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 3, time(), $record);
     207          $userevent = $this->create_calendar_event('user', $USER->id);
     208          $record = new stdClass();
     209          $record->courseid = $course->id;
     210          $record->groupid = $group->id;
     211          $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record);
     212  
     213          $this->setuser($user);
     214          $sitecontext = context_system::instance();
     215          $coursecontext = context_course::instance($course->id);
     216          $usercontext = context_user::instance($user->id);
     217          $role = $DB->get_record('role', array('shortname' => 'student'));
     218          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
     219  
     220          // Remove all caps.
     221          $this->unassignUserCapability('moodle/calendar:manageentries', $sitecontext->id, $role->id);
     222          $this->unassignUserCapability('moodle/calendar:manageentries', $coursecontext->id, $role->id);
     223          $this->unassignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
     224          $this->unassignUserCapability('moodle/calendar:manageownentries', $usercontext->id, $role->id);
     225  
     226          // Assign proper caps and attempt delete.
     227           $this->assignUserCapability('moodle/calendar:manageentries', $sitecontext->id, $role->id);
     228           $events = array(
     229                  array('eventid' => $siteevent->id, 'repeat' => 0),
     230                  );
     231          core_calendar_external::delete_calendar_events($events);
     232          $deletedcount = $DB->count_records('event');
     233          $count = $notdeletedcount+5;
     234          $this->assertEquals($count, $deletedcount);
     235  
     236           $this->assignUserCapability('moodle/calendar:manageentries', $sitecontext->id, $role->id);
     237           $events = array(
     238                  array('eventid' => $courseevent->id, 'repeat' => 0),
     239                  );
     240          core_calendar_external::delete_calendar_events($events);
     241          $deletedcount = $DB->count_records('event');
     242          $count = $notdeletedcount+4;
     243          $this->assertEquals($count, $deletedcount);
     244  
     245           $this->assignUserCapability('moodle/calendar:manageownentries', $usercontext->id, $role->id);
     246           $events = array(
     247                  array('eventid' => $userevent->id, 'repeat' => 0),
     248                  );
     249          core_calendar_external::delete_calendar_events($events);
     250          $deletedcount = $DB->count_records('event');
     251          $count = $notdeletedcount+3;
     252          $this->assertEquals($count, $deletedcount);
     253  
     254           $this->assignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
     255           $events = array(
     256                  array('eventid' => $groupevent->id, 'repeat' => 0),
     257                  );
     258          core_calendar_external::delete_calendar_events($events);
     259          $deletedcount = $DB->count_records('event');
     260          $count = $notdeletedcount+2;
     261          $this->assertEquals($count, $deletedcount);
     262  
     263          $notdeletedcount = $deletedcount;
     264  
     265          // Let us try deleting without caps.
     266  
     267          $siteevent = $this->create_calendar_event('site', $USER->id, 'site');
     268          $record = new stdClass();
     269          $record->courseid = $course->id;
     270          $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 3, time(), $record);
     271          $userevent = $this->create_calendar_event('user', $USER->id);
     272          $record = new stdClass();
     273          $record->courseid = $course->id;
     274          $record->groupid = $group->id;
     275          $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record);
     276  
     277          $this->setGuestUser();
     278  
     279          $events = array(
     280              array('eventid' => $siteevent->id, 'repeat' => 0),
     281              array('eventid' => $courseevent->id, 'repeat' => 0),
     282              array('eventid' => $userevent->id, 'repeat' => 0),
     283              array('eventid' => $groupevent->id, 'repeat' => 0)
     284          );
     285          core_calendar_external::delete_calendar_events($events);
     286      }
     287  
     288      /**
     289       * Test get_calendar_events
     290       */
     291      public function test_get_calendar_events() {
     292          global $DB, $USER;
     293  
     294          $this->resetAfterTest(true);
     295          set_config('calendar_adminseesall', 1);
     296          $this->setAdminUser();
     297  
     298          // Create a few stuff to test with.
     299          $user = $this->getDataGenerator()->create_user();
     300          $user2 = $this->getDataGenerator()->create_user();
     301          $course = $this->getDataGenerator()->create_course();
     302  
     303          $category = $this->getDataGenerator()->create_category();
     304  
     305          $category2 = $this->getDataGenerator()->create_category();
     306          $category2b = $this->getDataGenerator()->create_category(['parent' => $category2->id]);
     307          $course3 = $this->getDataGenerator()->create_course(['category' => $category2b->id]);
     308  
     309          $role = $DB->get_record('role', array('shortname' => 'student'));
     310          $this->getDataGenerator()->enrol_user($user2->id, $course3->id, $role->id);
     311  
     312          $record = new stdClass();
     313          $record->courseid = $course->id;
     314          $group = $this->getDataGenerator()->create_group($record);
     315  
     316          $beforecount = $DB->count_records('event');
     317  
     318          // Let's create a few events.
     319          $siteevent = $this->create_calendar_event('site', $USER->id, 'site');
     320  
     321          // This event will have description with an inline fake image.
     322          $draftidfile = file_get_unused_draft_itemid();
     323          $usercontext = context_course::instance($course->id);
     324          $filerecord = array(
     325              'contextid' => $usercontext->id,
     326              'component' => 'user',
     327              'filearea'  => 'draft',
     328              'itemid'    => $draftidfile,
     329              'filepath'  => '/',
     330              'filename'  => 'fakeimage.png',
     331          );
     332          $fs = get_file_storage();
     333          $fs->create_file_from_string($filerecord, 'img contents');
     334  
     335          $record = new stdClass();
     336          $record->courseid = $course->id;
     337          $record->groupid = 0;
     338          $record->description = array(
     339              'format' => FORMAT_HTML,
     340              'text' => 'Text with img <img src="@@PLUGINFILE@@/fakeimage.png">',
     341              'itemid' => $draftidfile
     342          );
     343          $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 2, time(), $record);
     344  
     345          $record = new stdClass();
     346          $record->courseid = 0;
     347          $record->groupid = 0;
     348          $userevent = $this->create_calendar_event('user', $USER->id, 'user', 0, time(), $record);
     349  
     350          $record = new stdClass();
     351          $record->courseid = $course->id;
     352          $record->groupid = $group->id;
     353          $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record);
     354  
     355          $paramevents = array ('eventids' => array($siteevent->id), 'courseids' => array($course->id),
     356                  'groupids' => array($group->id), 'categoryids' => array($category->id));
     357  
     358          $options = array ('siteevents' => true, 'userevents' => true);
     359          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     360          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     361  
     362          // Check to see if we got all events.
     363          $this->assertEquals(5, count($events['events']));
     364          $this->assertEquals(0, count($events['warnings']));
     365          $options = array ('siteevents' => true, 'userevents' => true, 'timeend' => time() + 7*WEEKSECS);
     366          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     367          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     368          $this->assertEquals(5, count($events['events']));
     369          $this->assertEquals(0, count($events['warnings']));
     370  
     371          // Expect the same URL in the description of two different events (because they are repeated).
     372          $coursecontext = context_course::instance($course->id);
     373          $expectedurl = "webservice/pluginfile.php/$coursecontext->id/calendar/event_description/$courseevent->id/fakeimage.png";
     374          $withdescription = 0;
     375          foreach ($events['events'] as $event) {
     376              if (!empty($event['description'])) {
     377                  $withdescription++;
     378                  $this->assertContains($expectedurl, $event['description']);
     379              }
     380          }
     381          $this->assertEquals(2, $withdescription);
     382  
     383          // Let's play around with caps.
     384  
     385          // Create user event for the user $user.
     386          $record = new stdClass();
     387          $record->courseid = 0;
     388          $record->groupid = 0;
     389          $this->create_calendar_event('user', $user->id, 'user', 0, time(), $record);
     390  
     391          $this->setUser($user);
     392          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     393          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     394          $this->assertEquals(2, count($events['events'])); // site, user.
     395          $this->assertEquals(2, count($events['warnings'])); // course, group.
     396  
     397          $role = $DB->get_record('role', array('shortname' => 'student'));
     398          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
     399          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     400          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     401          $this->assertEquals(4, count($events['events'])); // site, user, both course events.
     402          $this->assertEquals(1, count($events['warnings'])); // group.
     403  
     404          $options = array ('siteevents' => true, 'userevents' => true, 'timeend' => time() + HOURSECS);
     405          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     406          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     407          $this->assertEquals(3, count($events['events'])); // site, user, one course event.
     408          $this->assertEquals(1, count($events['warnings'])); // group.
     409  
     410          groups_add_member($group, $user);
     411          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     412          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     413          $this->assertEquals(4, count($events['events'])); // site, user, group, one course event.
     414          $this->assertEquals(0, count($events['warnings']));
     415  
     416          $paramevents = array ('courseids' => array($course->id), 'groupids' => array($group->id));
     417          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     418          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     419          $this->assertEquals(4, count($events['events'])); // site, user, group, one course event.
     420          $this->assertEquals(0, count($events['warnings']));
     421  
     422          $paramevents = array ('groupids' => array($group->id, 23));
     423          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     424          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     425          $this->assertEquals(3, count($events['events'])); // site, user, group.
     426          $this->assertEquals(1, count($events['warnings']));
     427  
     428          $paramevents = array ('courseids' => array(23));
     429          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     430          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     431          $this->assertEquals(2, count($events['events'])); // site, user.
     432          $this->assertEquals(1, count($events['warnings']));
     433  
     434          $paramevents = array ();
     435          $options = array ('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7*WEEKSECS);
     436          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     437          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     438          $this->assertEquals(0, count($events['events'])); // nothing returned.
     439          $this->assertEquals(0, count($events['warnings']));
     440  
     441          $paramevents = array ('eventids' => array($siteevent->id, $groupevent->id));
     442          $options = array ('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7*WEEKSECS);
     443          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     444          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     445          $this->assertEquals(2, count($events['events'])); // site, group.
     446          $this->assertEquals(0, count($events['warnings']));
     447  
     448          $paramevents = array ('eventids' => array($siteevent->id));
     449          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     450          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     451          $this->assertEquals(1, count($events['events'])); // site.
     452          $this->assertEquals(0, count($events['warnings']));
     453  
     454          // Try getting a course event by its id.
     455          $paramevents = array ('eventids' => array($courseevent->id));
     456          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     457          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     458          $this->assertEquals(1, count($events['events']));
     459          $this->assertEquals(0, count($events['warnings']));
     460  
     461          // Now, create an activity event.
     462          $this->setAdminUser();
     463          $nexttime = time() + DAYSECS;
     464          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'duedate' => $nexttime));
     465  
     466          $this->setUser($user);
     467          $paramevents = array ('courseids' => array($course->id));
     468          $options = array ('siteevents' => true, 'userevents' => true, 'timeend' => time() + WEEKSECS);
     469          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     470          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     471  
     472          $this->assertCount(5, $events['events']);
     473  
     474          // Hide the assignment.
     475          set_coursemodule_visible($assign->cmid, 0);
     476          // Empty all the caches that may be affected  by this change.
     477          accesslib_clear_all_caches_for_unit_testing();
     478          course_modinfo::clear_instance_cache();
     479  
     480          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     481          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     482          // Expect one less.
     483          $this->assertCount(4, $events['events']);
     484  
     485          // Create some category events.
     486          $this->setAdminUser();
     487          $record = new stdClass();
     488          $record->courseid = 0;
     489          $record->categoryid = $category->id;
     490          $record->timestart = time() - DAYSECS;
     491          $catevent1 = $this->create_calendar_event('category a', $USER->id, 'category', 0, time(), $record);
     492  
     493          $record = new stdClass();
     494          $record->courseid = 0;
     495          $record->categoryid = $category2->id;
     496          $record->timestart = time() + DAYSECS;
     497          $catevent2 = $this->create_calendar_event('category b', $USER->id, 'category', 0, time(), $record);
     498  
     499          // Now as student, make sure we get the events of the courses I am enrolled.
     500          $this->setUser($user2);
     501          $paramevents = array('categoryids' => array($category2b->id));
     502          $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
     503          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     504          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     505  
     506          // Should be just one, since there's just one category event of the course I am enrolled (course3 - cat2b).
     507          $this->assertEquals(1, count($events['events']));
     508          $this->assertEquals($catevent2->id, $events['events'][0]['id']);
     509          $this->assertEquals($category2->id, $events['events'][0]['categoryid']);
     510          $this->assertEquals(0, count($events['warnings']));
     511  
     512          // Now get category events but by course (there aren't course events in the course).
     513          $paramevents = array('courseids' => array($course3->id));
     514          $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
     515          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     516          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     517          $this->assertEquals(1, count($events['events']));
     518          $this->assertEquals($catevent2->id, $events['events'][0]['id']);
     519          $this->assertEquals(0, count($events['warnings']));
     520  
     521          // Empty events in one where I'm not enrolled and one parent category
     522          // (parent of a category where this is a course where the user is enrolled).
     523          $paramevents = array('categoryids' => array($category2->id, $category->id));
     524          $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
     525          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     526          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     527          $this->assertEquals(1, count($events['events']));
     528          $this->assertEquals($catevent2->id, $events['events'][0]['id']);
     529          $this->assertEquals(0, count($events['warnings']));
     530  
     531          // Admin can see all category events.
     532          $this->setAdminUser();
     533          $paramevents = array('categoryids' => array($category->id, $category2->id, $category2b->id));
     534          $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
     535          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     536          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     537          $this->assertEquals(2, count($events['events']));
     538          $this->assertEquals(0, count($events['warnings']));
     539          $this->assertEquals($catevent1->id, $events['events'][0]['id']);
     540          $this->assertEquals($category->id, $events['events'][0]['categoryid']);
     541          $this->assertEquals($catevent2->id, $events['events'][1]['id']);
     542          $this->assertEquals($category2->id, $events['events'][1]['categoryid']);
     543      }
     544  
     545      /**
     546       * Test core_calendar_external::create_calendar_events
     547       */
     548      public function test_core_create_calendar_events() {
     549          global $DB, $USER, $SITE;
     550  
     551          $this->resetAfterTest(true);
     552          $this->setAdminUser();
     553  
     554          // Create a few stuff to test with.
     555          $user = $this->getDataGenerator()->create_user();
     556          $course = $this->getDataGenerator()->create_course();
     557          $record = new stdClass();
     558          $record->courseid = $course->id;
     559          $group = $this->getDataGenerator()->create_group($record);
     560  
     561          $prevcount = $DB->count_records('event');
     562  
     563          // Let's create a few events.
     564          $events = array (
     565                  array('name' => 'site', 'courseid' => $SITE->id, 'eventtype' => 'site'),
     566                  array('name' => 'course', 'courseid' => $course->id, 'eventtype' => 'course', 'repeats' => 2),
     567                  array('name' => 'group', 'courseid' => $course->id, 'groupid' => $group->id, 'eventtype' => 'group'),
     568                  array('name' => 'user')
     569                  );
     570          $eventsret = core_calendar_external::create_calendar_events($events);
     571          $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
     572  
     573          // Check to see if things were created properly.
     574          $aftercount = $DB->count_records('event');
     575          $this->assertEquals($prevcount + 5, $aftercount);
     576          $this->assertEquals(5, count($eventsret['events']));
     577          $this->assertEquals(0, count($eventsret['warnings']));
     578  
     579          $sitecontext = context_system::instance();
     580          $coursecontext = context_course::instance($course->id);
     581  
     582          $this->setUser($user);
     583          $prevcount = $aftercount;
     584          $events = array (
     585                  array('name' => 'course', 'courseid' => $course->id, 'eventtype' => 'course', 'repeats' => 2),
     586                  array('name' => 'group', 'courseid' => $course->id, 'groupid' => $group->id, 'eventtype' => 'group'),
     587                  array('name' => 'user')
     588          );
     589          $role = $DB->get_record('role', array('shortname' => 'student'));
     590          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
     591          groups_add_member($group, $user);
     592          $this->assignUserCapability('moodle/calendar:manageentries', $coursecontext->id, $role->id);
     593          $this->assignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
     594          $eventsret = core_calendar_external::create_calendar_events($events);
     595          $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
     596          // Check to see if things were created properly.
     597          $aftercount = $DB->count_records('event');
     598          $this->assertEquals($prevcount + 4, $aftercount);
     599          $this->assertEquals(4, count($eventsret['events']));
     600          $this->assertEquals(0, count($eventsret['warnings']));
     601  
     602          // Check to see nothing was created without proper permission.
     603          $this->setGuestUser();
     604          $prevcount = $DB->count_records('event');
     605          $eventsret = core_calendar_external::create_calendar_events($events);
     606          $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
     607          $aftercount = $DB->count_records('event');
     608          $this->assertEquals($prevcount, $aftercount);
     609          $this->assertEquals(0, count($eventsret['events']));
     610          $this->assertEquals(3, count($eventsret['warnings']));
     611  
     612          $this->setUser($user);
     613          $this->unassignUserCapability('moodle/calendar:manageentries', $coursecontext->id, $role->id);
     614          $this->unassignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
     615          $prevcount = $DB->count_records('event');
     616          $eventsret = core_calendar_external::create_calendar_events($events);
     617          $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
     618          $aftercount = $DB->count_records('event');
     619          $this->assertEquals($prevcount + 1, $aftercount); // User event.
     620          $this->assertEquals(1, count($eventsret['events']));
     621          $this->assertEquals(2, count($eventsret['warnings']));
     622      }
     623  
     624      /**
     625       * Requesting calendar events from a given time should return all events with a sort
     626       * time at or after the requested time. All events prior to that time should not
     627       * be return.
     628       *
     629       * If there are no events on or after the given time then an empty result set should
     630       * be returned.
     631       */
     632      public function test_get_calendar_action_events_by_timesort_after_time() {
     633          $user = $this->getDataGenerator()->create_user();
     634          $course = $this->getDataGenerator()->create_course();
     635          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     636          $moduleinstance = $generator->create_instance(['course' => $course->id]);
     637  
     638          $this->getDataGenerator()->enrol_user($user->id, $course->id);
     639          $this->resetAfterTest(true);
     640          $this->setUser($user);
     641  
     642          $params = [
     643              'type' => CALENDAR_EVENT_TYPE_ACTION,
     644              'modulename' => 'assign',
     645              'instance' => $moduleinstance->id,
     646              'courseid' => $course->id,
     647          ];
     648  
     649          $event1 = $this->create_calendar_event('Event 1', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 1]));
     650          $event2 = $this->create_calendar_event('Event 2', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 2]));
     651          $event3 = $this->create_calendar_event('Event 3', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 3]));
     652          $event4 = $this->create_calendar_event('Event 4', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 4]));
     653          $event5 = $this->create_calendar_event('Event 5', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 5]));
     654          $event6 = $this->create_calendar_event('Event 6', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 6]));
     655          $event7 = $this->create_calendar_event('Event 7', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 7]));
     656          $event8 = $this->create_calendar_event('Event 8', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 8]));
     657  
     658          $result = core_calendar_external::get_calendar_action_events_by_timesort(5);
     659          $result = external_api::clean_returnvalue(
     660              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     661              $result
     662          );
     663          $events = $result['events'];
     664  
     665          $this->assertCount(4, $events);
     666          $this->assertEquals('Event 5', $events[0]['name']);
     667          $this->assertEquals('Event 6', $events[1]['name']);
     668          $this->assertEquals('Event 7', $events[2]['name']);
     669          $this->assertEquals('Event 8', $events[3]['name']);
     670          $this->assertEquals($event5->id, $result['firstid']);
     671          $this->assertEquals($event8->id, $result['lastid']);
     672  
     673          $result = core_calendar_external::get_calendar_action_events_by_timesort(9);
     674          $result = external_api::clean_returnvalue(
     675              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     676              $result
     677          );
     678  
     679          $this->assertEmpty($result['events']);
     680          $this->assertNull($result['firstid']);
     681          $this->assertNull($result['lastid']);
     682  
     683          // Requesting action events on behalf of another user.
     684          $this->setAdminUser();
     685          $result = core_calendar_external::get_calendar_action_events_by_timesort(5, null, 0, 20, false, $user->id);
     686          $result = external_api::clean_returnvalue(
     687              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     688              $result
     689          );
     690          $events = $result['events'];
     691  
     692          $this->assertCount(4, $events);
     693          $this->assertEquals('Event 5', $events[0]['name']);
     694          $this->assertEquals('Event 6', $events[1]['name']);
     695          $this->assertEquals('Event 7', $events[2]['name']);
     696          $this->assertEquals('Event 8', $events[3]['name']);
     697          $this->assertEquals($event5->id, $result['firstid']);
     698          $this->assertEquals($event8->id, $result['lastid']);
     699      }
     700  
     701      /**
     702       * Requesting calendar events before a given time should return all events with a sort
     703       * time at or before the requested time (inclusive). All events after that time
     704       * should not be returned.
     705       *
     706       * If there are no events before the given time then an empty result set should be
     707       * returned.
     708       */
     709      public function test_get_calendar_action_events_by_timesort_before_time() {
     710          $user = $this->getDataGenerator()->create_user();
     711          $course = $this->getDataGenerator()->create_course();
     712          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     713          $moduleinstance = $generator->create_instance(['course' => $course->id]);
     714  
     715          $this->getDataGenerator()->enrol_user($user->id, $course->id);
     716          $this->resetAfterTest(true);
     717          $this->setUser($user);
     718  
     719          $params = [
     720              'type' => CALENDAR_EVENT_TYPE_ACTION,
     721              'modulename' => 'assign',
     722              'instance' => $moduleinstance->id,
     723              'courseid' => $course->id,
     724          ];
     725  
     726          $event1 = $this->create_calendar_event('Event 1', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 2]));
     727          $event2 = $this->create_calendar_event('Event 2', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 3]));
     728          $event3 = $this->create_calendar_event('Event 3', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 4]));
     729          $event4 = $this->create_calendar_event('Event 4', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 5]));
     730          $event5 = $this->create_calendar_event('Event 5', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 6]));
     731          $event6 = $this->create_calendar_event('Event 6', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 7]));
     732          $event7 = $this->create_calendar_event('Event 7', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 8]));
     733          $event8 = $this->create_calendar_event('Event 8', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 9]));
     734  
     735          $result = core_calendar_external::get_calendar_action_events_by_timesort(null, 5);
     736          $result = external_api::clean_returnvalue(
     737              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     738              $result
     739          );
     740          $events = $result['events'];
     741  
     742          $this->assertCount(4, $events);
     743          $this->assertEquals('Event 1', $events[0]['name']);
     744          $this->assertEquals('Event 2', $events[1]['name']);
     745          $this->assertEquals('Event 3', $events[2]['name']);
     746          $this->assertEquals('Event 4', $events[3]['name']);
     747          $this->assertEquals($event1->id, $result['firstid']);
     748          $this->assertEquals($event4->id, $result['lastid']);
     749  
     750          $result = core_calendar_external::get_calendar_action_events_by_timesort(null, 1);
     751          $result = external_api::clean_returnvalue(
     752              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     753              $result
     754          );
     755  
     756          $this->assertEmpty($result['events']);
     757          $this->assertNull($result['firstid']);
     758          $this->assertNull($result['lastid']);
     759  
     760          // Requesting action events on behalf of another user.
     761          $this->setAdminUser();
     762  
     763          $result = core_calendar_external::get_calendar_action_events_by_timesort(null, 5, 0, 20, false, $user->id);
     764          $result = external_api::clean_returnvalue(
     765              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     766              $result
     767          );
     768          $events = $result['events'];
     769  
     770          $this->assertCount(4, $events);
     771          $this->assertEquals('Event 1', $events[0]['name']);
     772          $this->assertEquals('Event 2', $events[1]['name']);
     773          $this->assertEquals('Event 3', $events[2]['name']);
     774          $this->assertEquals('Event 4', $events[3]['name']);
     775          $this->assertEquals($event1->id, $result['firstid']);
     776          $this->assertEquals($event4->id, $result['lastid']);
     777      }
     778  
     779      /**
     780       * Test retrieving event that was overridden for a user
     781       */
     782      public function test_get_calendar_events_override() {
     783          $user = $this->getDataGenerator()->create_user();
     784          $user2 = $this->getDataGenerator()->create_user();
     785          $teacher = $this->getDataGenerator()->create_user();
     786          $anotheruser = $this->getDataGenerator()->create_user();
     787          $course = $this->getDataGenerator()->create_course();
     788          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     789          $moduleinstance = $generator->create_instance(['course' => $course->id]);
     790  
     791          $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
     792          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
     793          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
     794          $this->resetAfterTest(true);
     795          $this->setAdminUser();
     796  
     797          $params = [
     798              'type' => CALENDAR_EVENT_TYPE_ACTION,
     799              'modulename' => 'assign',
     800              'instance' => $moduleinstance->id,
     801          ];
     802  
     803          $now = time();
     804          // Create two events - one for everybody in the course and one only for the first student.
     805          $event1 = $this->create_calendar_event('Base event', 0, 'due', 0, $now + DAYSECS, $params + ['courseid' => $course->id]);
     806          $event2 = $this->create_calendar_event('User event', $user->id, 'due', 0, $now + 2*DAYSECS, $params + ['courseid' => 0]);
     807  
     808          // Retrieve course events for the second student - only one "Base event" is returned.
     809          $this->setUser($user2);
     810          $paramevents = array('courseids' => array($course->id));
     811          $options = array ('siteevents' => true, 'userevents' => true);
     812          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     813          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     814          $this->assertEquals(1, count($events['events']));
     815          $this->assertEquals(0, count($events['warnings']));
     816          $this->assertEquals('Base event', $events['events'][0]['name']);
     817  
     818          // Retrieve events for the first student - both events are returned.
     819          $this->setUser($user);
     820          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     821          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     822          $this->assertEquals(2, count($events['events']));
     823          $this->assertEquals(0, count($events['warnings']));
     824          $this->assertEquals('Base event', $events['events'][0]['name']);
     825          $this->assertEquals('User event', $events['events'][1]['name']);
     826  
     827          // Retrieve events by id as a teacher, 'User event' should be returned since teacher has access to this course.
     828          $this->setUser($teacher);
     829          $paramevents = ['eventids' => [$event2->id]];
     830          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     831          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     832          $this->assertEquals(1, count($events['events']));
     833          $this->assertEquals(0, count($events['warnings']));
     834          $this->assertEquals('User event', $events['events'][0]['name']);
     835  
     836          // Retrieve events by id as another user, nothing should be returned.
     837          $this->setUser($anotheruser);
     838          $paramevents = ['eventids' => [$event2->id, $event1->id]];
     839          $events = core_calendar_external::get_calendar_events($paramevents, $options);
     840          $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
     841          $this->assertEquals(0, count($events['events']));
     842          $this->assertEquals(0, count($events['warnings']));
     843      }
     844  
     845      /**
     846       * Requesting calendar events within a given time range should return all events with
     847       * a sort time between the lower and upper time bound (inclusive).
     848       *
     849       * If there are no events in the given time range then an empty result set should be
     850       * returned.
     851       */
     852      public function test_get_calendar_action_events_by_timesort_time_range() {
     853          $user = $this->getDataGenerator()->create_user();
     854          $course = $this->getDataGenerator()->create_course();
     855          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     856          $moduleinstance = $generator->create_instance(['course' => $course->id]);
     857  
     858          $this->getDataGenerator()->enrol_user($user->id, $course->id);
     859          $this->resetAfterTest(true);
     860          $this->setUser($user);
     861  
     862          $params = [
     863              'type' => CALENDAR_EVENT_TYPE_ACTION,
     864              'modulename' => 'assign',
     865              'instance' => $moduleinstance->id,
     866              'courseid' => $course->id,
     867          ];
     868  
     869          $event1 = $this->create_calendar_event('Event 1', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 1]));
     870          $event2 = $this->create_calendar_event('Event 2', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 2]));
     871          $event3 = $this->create_calendar_event('Event 3', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 3]));
     872          $event4 = $this->create_calendar_event('Event 4', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 4]));
     873          $event5 = $this->create_calendar_event('Event 5', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 5]));
     874          $event6 = $this->create_calendar_event('Event 6', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 6]));
     875          $event7 = $this->create_calendar_event('Event 7', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 7]));
     876          $event8 = $this->create_calendar_event('Event 8', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 8]));
     877  
     878          $result = core_calendar_external::get_calendar_action_events_by_timesort(3, 6);
     879          $result = external_api::clean_returnvalue(
     880              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     881              $result
     882          );
     883          $events = $result['events'];
     884  
     885          $this->assertCount(4, $events);
     886          $this->assertEquals('Event 3', $events[0]['name']);
     887          $this->assertEquals('Event 4', $events[1]['name']);
     888          $this->assertEquals('Event 5', $events[2]['name']);
     889          $this->assertEquals('Event 6', $events[3]['name']);
     890          $this->assertEquals($event3->id, $result['firstid']);
     891          $this->assertEquals($event6->id, $result['lastid']);
     892  
     893          $result = core_calendar_external::get_calendar_action_events_by_timesort(10, 15);
     894          $result = external_api::clean_returnvalue(
     895              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     896              $result
     897          );
     898  
     899          $this->assertEmpty($result['events']);
     900          $this->assertNull($result['firstid']);
     901          $this->assertNull($result['lastid']);
     902      }
     903  
     904      /**
     905       * Requesting calendar events within a given time range and a limit and offset should return
     906       * the number of events up to the given limit value that have a sort time between the lower
     907       * and uppper time bound (inclusive) where the result set is shifted by the offset value.
     908       *
     909       * If there are no events in the given time range then an empty result set should be
     910       * returned.
     911       */
     912      public function test_get_calendar_action_events_by_timesort_time_limit_offset() {
     913          $user = $this->getDataGenerator()->create_user();
     914          $course = $this->getDataGenerator()->create_course();
     915          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     916          $moduleinstance = $generator->create_instance(['course' => $course->id]);
     917  
     918          $this->getDataGenerator()->enrol_user($user->id, $course->id);
     919          $this->resetAfterTest(true);
     920          $this->setUser($user);
     921  
     922          $params = [
     923              'type' => CALENDAR_EVENT_TYPE_ACTION,
     924              'modulename' => 'assign',
     925              'instance' => $moduleinstance->id,
     926              'courseid' => $course->id,
     927          ];
     928  
     929          $event1 = $this->create_calendar_event('Event 1', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 1]));
     930          $event2 = $this->create_calendar_event('Event 2', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 2]));
     931          $event3 = $this->create_calendar_event('Event 3', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 3]));
     932          $event4 = $this->create_calendar_event('Event 4', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 4]));
     933          $event5 = $this->create_calendar_event('Event 5', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 5]));
     934          $event6 = $this->create_calendar_event('Event 6', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 6]));
     935          $event7 = $this->create_calendar_event('Event 7', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 7]));
     936          $event8 = $this->create_calendar_event('Event 8', $user->id, 'user', 0, 1, array_merge($params, ['timesort' => 8]));
     937  
     938          $result = core_calendar_external::get_calendar_action_events_by_timesort(2, 7, $event3->id, 2);
     939          $result = external_api::clean_returnvalue(
     940              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     941              $result
     942          );
     943          $events = $result['events'];
     944  
     945          $this->assertCount(2, $events);
     946          $this->assertEquals('Event 4', $events[0]['name']);
     947          $this->assertEquals('Event 5', $events[1]['name']);
     948          $this->assertEquals($event4->id, $result['firstid']);
     949          $this->assertEquals($event5->id, $result['lastid']);
     950  
     951          $result = core_calendar_external::get_calendar_action_events_by_timesort(2, 7, $event5->id, 2);
     952          $result = external_api::clean_returnvalue(
     953              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     954              $result
     955          );
     956          $events = $result['events'];
     957  
     958          $this->assertCount(2, $events);
     959          $this->assertEquals('Event 6', $events[0]['name']);
     960          $this->assertEquals('Event 7', $events[1]['name']);
     961          $this->assertEquals($event6->id, $result['firstid']);
     962          $this->assertEquals($event7->id, $result['lastid']);
     963  
     964          $result = core_calendar_external::get_calendar_action_events_by_timesort(2, 7, $event7->id, 2);
     965          $result = external_api::clean_returnvalue(
     966              core_calendar_external::get_calendar_action_events_by_timesort_returns(),
     967              $result
     968          );
     969  
     970          $this->assertEmpty($result['events']);
     971          $this->assertNull($result['firstid']);
     972          $this->assertNull($result['lastid']);
     973      }
     974  
     975      /**
     976       * Check that it is possible to restrict the calendar events to events where the user is not suspended in the course.
     977       */
     978      public function test_get_calendar_action_events_by_timesort_suspended_course() {
     979          $this->resetAfterTest();
     980          $user1 = $this->getDataGenerator()->create_user();
     981          $user2 = $this->getDataGenerator()->create_user();
     982          $course = $this->getDataGenerator()->create_course();
     983          $this->setAdminUser();
     984          $lesson = $this->getDataGenerator()->create_module('lesson', [
     985                  'name' => 'Lesson 1',
     986                  'course' => $course->id,
     987                  'available' => time(),
     988                  'deadline' => (time() + (60 * 60 * 24 * 5))
     989              ]
     990          );
     991          $this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
     992          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
     993  
     994          $this->setUser($user1);
     995          $result = core_calendar_external::get_calendar_action_events_by_timesort(0, null, 0, 20, true);
     996          $this->assertEmpty($result->events);
     997          $this->setUser($user2);
     998          $result = core_calendar_external::get_calendar_action_events_by_timesort(0, null, 0, 20, true);
     999          $this->assertCount(1, $result->events);
    1000          $this->assertEquals('Lesson 1 closes', $result->events[0]->name);
    1001      }
    1002  
    1003      /**
    1004       * Requesting calendar events from a given course and time should return all
    1005       * events with a sort time at or after the requested time. All events prior
    1006       * to that time should not be return.
    1007       *
    1008       * If there are no events on or after the given time then an empty result set should
    1009       * be returned.
    1010       */
    1011      public function test_get_calendar_action_events_by_course_after_time() {
    1012          $user = $this->getDataGenerator()->create_user();
    1013          $course1 = $this->getDataGenerator()->create_course();
    1014          $course2 = $this->getDataGenerator()->create_course();
    1015          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1016          $instance1 = $generator->create_instance(['course' => $course1->id]);
    1017          $instance2 = $generator->create_instance(['course' => $course2->id]);
    1018          $records = [];
    1019  
    1020          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
    1021          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
    1022          $this->resetAfterTest(true);
    1023          $this->setUser($user);
    1024  
    1025          for ($i = 1; $i < 19; $i++) {
    1026              $courseid = ($i < 9) ? $course1->id : $course2->id;
    1027              $instance = ($i < 9) ? $instance1->id : $instance2->id;
    1028              $records[] = $this->create_calendar_event(
    1029                  sprintf('Event %d', $i),
    1030                  $user->id,
    1031                  'user',
    1032                  0,
    1033                  1,
    1034                  [
    1035                      'type' => CALENDAR_EVENT_TYPE_ACTION,
    1036                      'courseid' => $courseid,
    1037                      'timesort' => $i,
    1038                      'modulename' => 'assign',
    1039                      'instance' => $instance,
    1040                  ]
    1041              );
    1042          }
    1043  
    1044          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, 5);
    1045          $result = external_api::clean_returnvalue(
    1046              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1047              $result
    1048          );
    1049          $result = $result['events'];
    1050  
    1051          $this->assertCount(4, $result);
    1052          $this->assertEquals('Event 5', $result[0]['name']);
    1053          $this->assertEquals('Event 6', $result[1]['name']);
    1054          $this->assertEquals('Event 7', $result[2]['name']);
    1055          $this->assertEquals('Event 8', $result[3]['name']);
    1056  
    1057          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, 9);
    1058          $result = external_api::clean_returnvalue(
    1059              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1060              $result
    1061          );
    1062          $result = $result['events'];
    1063  
    1064          $this->assertEmpty($result);
    1065      }
    1066  
    1067      /**
    1068       * Requesting calendar events for a course and before a given time should return
    1069       * all events with a sort time at or before the requested time (inclusive). All
    1070       * events after that time should not be returned.
    1071       *
    1072       * If there are no events before the given time then an empty result set should be
    1073       * returned.
    1074       */
    1075      public function test_get_calendar_action_events_by_course_before_time() {
    1076          $user = $this->getDataGenerator()->create_user();
    1077          $course1 = $this->getDataGenerator()->create_course();
    1078          $course2 = $this->getDataGenerator()->create_course();
    1079          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1080          $instance1 = $generator->create_instance(['course' => $course1->id]);
    1081          $instance2 = $generator->create_instance(['course' => $course2->id]);
    1082          $records = [];
    1083  
    1084          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
    1085          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
    1086          $this->resetAfterTest(true);
    1087          $this->setUser($user);
    1088  
    1089          for ($i = 1; $i < 19; $i++) {
    1090              $courseid = ($i < 9) ? $course1->id : $course2->id;
    1091              $instance = ($i < 9) ? $instance1->id : $instance2->id;
    1092              $records[] = $this->create_calendar_event(
    1093                  sprintf('Event %d', $i),
    1094                  $user->id,
    1095                  'user',
    1096                  0,
    1097                  1,
    1098                  [
    1099                      'type' => CALENDAR_EVENT_TYPE_ACTION,
    1100                      'courseid' => $courseid,
    1101                      'timesort' => $i + 1,
    1102                      'modulename' => 'assign',
    1103                      'instance' => $instance,
    1104                  ]
    1105              );
    1106          }
    1107  
    1108          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, null, 5);
    1109          $result = external_api::clean_returnvalue(
    1110              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1111              $result
    1112          );
    1113          $result = $result['events'];
    1114  
    1115          $this->assertCount(4, $result);
    1116          $this->assertEquals('Event 1', $result[0]['name']);
    1117          $this->assertEquals('Event 2', $result[1]['name']);
    1118          $this->assertEquals('Event 3', $result[2]['name']);
    1119          $this->assertEquals('Event 4', $result[3]['name']);
    1120  
    1121          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, null, 1);
    1122          $result = external_api::clean_returnvalue(
    1123              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1124              $result
    1125          );
    1126          $result = $result['events'];
    1127  
    1128          $this->assertEmpty($result);
    1129      }
    1130  
    1131      /**
    1132       * Requesting calendar events for a course and within a given time range should
    1133       * return all events with a sort time between the lower and upper time bound
    1134       * (inclusive).
    1135       *
    1136       * If there are no events in the given time range then an empty result set should be
    1137       * returned.
    1138       */
    1139      public function test_get_calendar_action_events_by_course_time_range() {
    1140          $user = $this->getDataGenerator()->create_user();
    1141          $course1 = $this->getDataGenerator()->create_course();
    1142          $course2 = $this->getDataGenerator()->create_course();
    1143          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1144          $instance1 = $generator->create_instance(['course' => $course1->id]);
    1145          $instance2 = $generator->create_instance(['course' => $course2->id]);
    1146          $records = [];
    1147  
    1148          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
    1149          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
    1150          $this->resetAfterTest(true);
    1151          $this->setUser($user);
    1152  
    1153          for ($i = 1; $i < 19; $i++) {
    1154              $courseid = ($i < 9) ? $course1->id : $course2->id;
    1155              $instance = ($i < 9) ? $instance1->id : $instance2->id;
    1156              $records[] = $this->create_calendar_event(
    1157                  sprintf('Event %d', $i),
    1158                  $user->id,
    1159                  'user',
    1160                  0,
    1161                  1,
    1162                  [
    1163                      'type' => CALENDAR_EVENT_TYPE_ACTION,
    1164                      'courseid' => $courseid,
    1165                      'timesort' => $i,
    1166                      'modulename' => 'assign',
    1167                      'instance' => $instance,
    1168                  ]
    1169              );
    1170          }
    1171  
    1172          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, 3, 6);
    1173          $result = external_api::clean_returnvalue(
    1174              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1175              $result
    1176          );
    1177          $result = $result['events'];
    1178  
    1179          $this->assertCount(4, $result);
    1180          $this->assertEquals('Event 3', $result[0]['name']);
    1181          $this->assertEquals('Event 4', $result[1]['name']);
    1182          $this->assertEquals('Event 5', $result[2]['name']);
    1183          $this->assertEquals('Event 6', $result[3]['name']);
    1184  
    1185          $result = core_calendar_external::get_calendar_action_events_by_course($course1->id, 10, 15);
    1186          $result = external_api::clean_returnvalue(
    1187              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1188              $result
    1189          );
    1190          $result = $result['events'];
    1191  
    1192          $this->assertEmpty($result);
    1193      }
    1194  
    1195      /**
    1196       * Requesting calendar events for a course and within a given time range and a limit
    1197       * and offset should return the number of events up to the given limit value that have
    1198       * a sort time between the lower and uppper time bound (inclusive) where the result
    1199       * set is shifted by the offset value.
    1200       *
    1201       * If there are no events in the given time range then an empty result set should be
    1202       * returned.
    1203       */
    1204      public function test_get_calendar_action_events_by_course_time_limit_offset() {
    1205          $user = $this->getDataGenerator()->create_user();
    1206          $course1 = $this->getDataGenerator()->create_course();
    1207          $course2 = $this->getDataGenerator()->create_course();
    1208          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1209          $instance1 = $generator->create_instance(['course' => $course1->id]);
    1210          $instance2 = $generator->create_instance(['course' => $course2->id]);
    1211          $records = [];
    1212  
    1213          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
    1214          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
    1215          $this->resetAfterTest(true);
    1216          $this->setUser($user);
    1217  
    1218          for ($i = 1; $i < 19; $i++) {
    1219              $courseid = ($i < 9) ? $course1->id : $course2->id;
    1220              $instance = ($i < 9) ? $instance1->id : $instance2->id;
    1221              $records[] = $this->create_calendar_event(
    1222                  sprintf('Event %d', $i),
    1223                  $user->id,
    1224                  'user',
    1225                  0,
    1226                  1,
    1227                  [
    1228                      'type' => CALENDAR_EVENT_TYPE_ACTION,
    1229                      'courseid' => $courseid,
    1230                      'timesort' => $i,
    1231                      'modulename' => 'assign',
    1232                      'instance' => $instance,
    1233                  ]
    1234              );
    1235          }
    1236  
    1237          $result = core_calendar_external::get_calendar_action_events_by_course(
    1238              $course1->id, 2, 7, $records[2]->id, 2);
    1239          $result = external_api::clean_returnvalue(
    1240              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1241              $result
    1242          );
    1243          $result = $result['events'];
    1244  
    1245          $this->assertCount(2, $result);
    1246          $this->assertEquals('Event 4', $result[0]['name']);
    1247          $this->assertEquals('Event 5', $result[1]['name']);
    1248  
    1249          $result = core_calendar_external::get_calendar_action_events_by_course(
    1250              $course1->id, 2, 7, $records[4]->id, 2);
    1251          $result = external_api::clean_returnvalue(
    1252              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1253              $result
    1254          );
    1255          $result = $result['events'];
    1256  
    1257          $this->assertCount(2, $result);
    1258          $this->assertEquals('Event 6', $result[0]['name']);
    1259          $this->assertEquals('Event 7', $result[1]['name']);
    1260  
    1261          $result = core_calendar_external::get_calendar_action_events_by_course(
    1262              $course1->id, 2, 7, $records[6]->id, 2);
    1263          $result = external_api::clean_returnvalue(
    1264              core_calendar_external::get_calendar_action_events_by_course_returns(),
    1265              $result
    1266          );
    1267          $result = $result['events'];
    1268  
    1269          $this->assertEmpty($result);
    1270      }
    1271  
    1272      /**
    1273       * Test that get_action_events_by_courses will return a list of events for each
    1274       * course you provided as long as the user is enrolled in the course.
    1275       */
    1276      public function test_get_action_events_by_courses() {
    1277          $user = $this->getDataGenerator()->create_user();
    1278          $course1 = $this->getDataGenerator()->create_course();
    1279          $course2 = $this->getDataGenerator()->create_course();
    1280          $course3 = $this->getDataGenerator()->create_course();
    1281          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1282          $instance1 = $generator->create_instance(['course' => $course1->id]);
    1283          $instance2 = $generator->create_instance(['course' => $course2->id]);
    1284          $instance3 = $generator->create_instance(['course' => $course3->id]);
    1285          $records = [];
    1286          $mapresult = function($result) {
    1287              $groupedbycourse = [];
    1288              foreach ($result['groupedbycourse'] as $group) {
    1289                  $events = $group['events'];
    1290                  $courseid = $group['courseid'];
    1291                  $groupedbycourse[$courseid] = $events;
    1292              }
    1293  
    1294              return $groupedbycourse;
    1295          };
    1296  
    1297          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
    1298          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
    1299          $this->resetAfterTest(true);
    1300          $this->setUser($user);
    1301  
    1302          for ($i = 1; $i < 10; $i++) {
    1303              if ($i < 3) {
    1304                  $courseid = $course1->id;
    1305                  $instance = $instance1->id;
    1306              } else if ($i < 6) {
    1307                  $courseid = $course2->id;
    1308                  $instance = $instance2->id;
    1309              } else {
    1310                  $courseid = $course3->id;
    1311                  $instance = $instance3->id;
    1312              }
    1313  
    1314              $records[] = $this->create_calendar_event(
    1315                  sprintf('Event %d', $i),
    1316                  $user->id,
    1317                  'user',
    1318                  0,
    1319                  1,
    1320                  [
    1321                      'type' => CALENDAR_EVENT_TYPE_ACTION,
    1322                      'courseid' => $courseid,
    1323                      'timesort' => $i,
    1324                      'modulename' => 'assign',
    1325                      'instance' => $instance,
    1326                  ]
    1327              );
    1328          }
    1329  
    1330          $result = core_calendar_external::get_calendar_action_events_by_courses([], 1);
    1331          $result = external_api::clean_returnvalue(
    1332              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1333              $result
    1334          );
    1335          $result = $result['groupedbycourse'];
    1336  
    1337          $this->assertEmpty($result);
    1338  
    1339          $result = core_calendar_external::get_calendar_action_events_by_courses([$course1->id], 3);
    1340          $result = external_api::clean_returnvalue(
    1341              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1342              $result
    1343          );
    1344  
    1345          $groupedbycourse = $mapresult($result);
    1346  
    1347          $this->assertEmpty($groupedbycourse[$course1->id]);
    1348  
    1349          $result = core_calendar_external::get_calendar_action_events_by_courses([$course1->id], 1);
    1350          $result = external_api::clean_returnvalue(
    1351              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1352              $result
    1353          );
    1354          $groupedbycourse = $mapresult($result);
    1355  
    1356          $this->assertCount(2, $groupedbycourse[$course1->id]);
    1357          $this->assertEquals('Event 1', $groupedbycourse[$course1->id][0]['name']);
    1358          $this->assertEquals('Event 2', $groupedbycourse[$course1->id][1]['name']);
    1359  
    1360          $result = core_calendar_external::get_calendar_action_events_by_courses(
    1361              [$course1->id, $course2->id], 1);
    1362          $result = external_api::clean_returnvalue(
    1363              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1364              $result
    1365          );
    1366          $groupedbycourse = $mapresult($result);
    1367  
    1368          $this->assertCount(2, $groupedbycourse[$course1->id]);
    1369          $this->assertEquals('Event 1', $groupedbycourse[$course1->id][0]['name']);
    1370          $this->assertEquals('Event 2', $groupedbycourse[$course1->id][1]['name']);
    1371          $this->assertCount(3, $groupedbycourse[$course2->id]);
    1372          $this->assertEquals('Event 3', $groupedbycourse[$course2->id][0]['name']);
    1373          $this->assertEquals('Event 4', $groupedbycourse[$course2->id][1]['name']);
    1374          $this->assertEquals('Event 5', $groupedbycourse[$course2->id][2]['name']);
    1375  
    1376          $result = core_calendar_external::get_calendar_action_events_by_courses(
    1377              [$course1->id, $course2->id], 2, 4);
    1378          $result = external_api::clean_returnvalue(
    1379              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1380              $result
    1381          );
    1382          $groupedbycourse = $mapresult($result);
    1383  
    1384          $this->assertCount(2, $groupedbycourse);
    1385          $this->assertCount(1, $groupedbycourse[$course1->id]);
    1386          $this->assertEquals('Event 2', $groupedbycourse[$course1->id][0]['name']);
    1387          $this->assertCount(2, $groupedbycourse[$course2->id]);
    1388          $this->assertEquals('Event 3', $groupedbycourse[$course2->id][0]['name']);
    1389          $this->assertEquals('Event 4', $groupedbycourse[$course2->id][1]['name']);
    1390  
    1391          $result = core_calendar_external::get_calendar_action_events_by_courses(
    1392              [$course1->id, $course2->id], 1, null, 1);
    1393          $result = external_api::clean_returnvalue(
    1394              core_calendar_external::get_calendar_action_events_by_courses_returns(),
    1395              $result
    1396          );
    1397          $groupedbycourse = $mapresult($result);
    1398  
    1399          $this->assertCount(2, $groupedbycourse);
    1400          $this->assertCount(1, $groupedbycourse[$course1->id]);
    1401          $this->assertEquals('Event 1', $groupedbycourse[$course1->id][0]['name']);
    1402          $this->assertCount(1, $groupedbycourse[$course2->id]);
    1403          $this->assertEquals('Event 3', $groupedbycourse[$course2->id][0]['name']);
    1404      }
    1405  
    1406      /**
    1407       * Test for deleting module events.
    1408       */
    1409      public function test_delete_calendar_events_for_modules() {
    1410          $this->resetAfterTest();
    1411          $this->setAdminUser();
    1412          $course = $this->getDataGenerator()->create_course();
    1413          $nexttime = time() + DAYSECS;
    1414          $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'duedate' => $nexttime]);
    1415          $events = calendar_get_events(time(), $nexttime, true, true, true);
    1416          $this->assertCount(1, $events);
    1417          $params = [];
    1418          foreach ($events as $event) {
    1419              $params[] = [
    1420                  'eventid' => $event->id,
    1421                  'repeat' => false
    1422              ];
    1423          }
    1424  
    1425          $this->expectException('moodle_exception');
    1426          core_calendar_external::delete_calendar_events($params);
    1427      }
    1428  
    1429      /**
    1430       * Updating the event start day should change the date value but leave
    1431       * the time of day unchanged.
    1432       */
    1433      public function test_update_event_start_day() {
    1434          $generator = $this->getDataGenerator();
    1435          $user = $generator->create_user();
    1436          $roleid = $generator->create_role();
    1437          $context = \context_system::instance();
    1438          $originalstarttime = new DateTimeImmutable('2017-01-1T15:00:00+08:00');
    1439          $newstartdate = new DateTimeImmutable('2018-02-2T10:00:00+08:00');
    1440          $expected = new DateTimeImmutable('2018-02-2T15:00:00+08:00');
    1441  
    1442          $generator->role_assign($roleid, $user->id, $context->id);
    1443          assign_capability('moodle/calendar:manageownentries', CAP_ALLOW, $roleid, $context, true);
    1444  
    1445          $this->setUser($user);
    1446          $this->resetAfterTest(true);
    1447  
    1448          $event = $this->create_calendar_event(
    1449              'Test event',
    1450              $user->id,
    1451              'user',
    1452              0,
    1453              null,
    1454              [
    1455                  'courseid' => 0,
    1456                  'timestart' => $originalstarttime->getTimestamp()
    1457              ]
    1458          );
    1459  
    1460          $result = core_calendar_external::update_event_start_day($event->id, $newstartdate->getTimestamp());
    1461          $result = external_api::clean_returnvalue(
    1462              core_calendar_external::update_event_start_day_returns(),
    1463              $result
    1464          );
    1465  
    1466          $this->assertEquals($expected->getTimestamp(), $result['event']['timestart']);
    1467      }
    1468  
    1469      /**
    1470       * A user should not be able to edit an event that they don't have
    1471       * capabilities for.
    1472       */
    1473      public function test_update_event_start_day_no_permission() {
    1474          $generator = $this->getDataGenerator();
    1475          $user = $generator->create_user();
    1476          $roleid = $generator->create_role();
    1477          $context = \context_system::instance();
    1478          $originalstarttime = new DateTimeImmutable('2017-01-1T15:00:00+08:00');
    1479          $newstartdate = new DateTimeImmutable('2018-02-2T10:00:00+08:00');
    1480          $expected = new DateTimeImmutable('2018-02-2T15:00:00+08:00');
    1481  
    1482          $generator->role_assign($roleid, $user->id, $context->id);
    1483          assign_capability('moodle/calendar:manageownentries', CAP_ALLOW, $roleid, $context, true);
    1484  
    1485          $this->setUser($user);
    1486          $this->resetAfterTest(true);
    1487  
    1488          $event = $this->create_calendar_event(
    1489              'Test event',
    1490              $user->id,
    1491              'user',
    1492              0,
    1493              null,
    1494              [
    1495                  'courseid' => 0,
    1496                  'timestart' => $originalstarttime->getTimestamp()
    1497              ]
    1498          );
    1499  
    1500          assign_capability('moodle/calendar:manageownentries', CAP_PROHIBIT, $roleid, $context, true);
    1501          $this->expectException('moodle_exception');
    1502          $result = core_calendar_external::update_event_start_day($event->id, $newstartdate->getTimestamp());
    1503          $result = external_api::clean_returnvalue(
    1504              core_calendar_external::update_event_start_day_returns(),
    1505              $result
    1506          );
    1507      }
    1508  
    1509      /**
    1510       * A user should not be able to update a module event.
    1511       */
    1512      public function test_update_event_start_day_module_event() {
    1513          $generator = $this->getDataGenerator();
    1514          $user = $generator->create_user();
    1515          $course = $generator->create_course();
    1516          $plugingenerator = $generator->get_plugin_generator('mod_assign');
    1517          $moduleinstance = $plugingenerator->create_instance(['course' => $course->id]);
    1518          $roleid = $generator->create_role();
    1519          $context = \context_course::instance($course->id);
    1520          $originalstarttime = new DateTimeImmutable('2017-01-1T15:00:00+08:00');
    1521          $newstartdate = new DateTimeImmutable('2018-02-2T10:00:00+08:00');
    1522          $expected = new DateTimeImmutable('2018-02-2T15:00:00+08:00');
    1523  
    1524          $generator->role_assign($roleid, $user->id, $context->id);
    1525          $generator->enrol_user($user->id, $course->id);
    1526  
    1527          $this->setUser($user);
    1528          $this->resetAfterTest(true);
    1529  
    1530          $event = $this->create_calendar_event(
    1531              'Test event',
    1532              $user->id,
    1533              'user',
    1534              0,
    1535              null,
    1536              [
    1537                  'modulename' => 'assign',
    1538                  'instance' => $moduleinstance->id,
    1539                  'courseid' => $course->id,
    1540                  'timestart' => $originalstarttime->getTimestamp()
    1541              ]
    1542          );
    1543  
    1544          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    1545          $this->expectException('moodle_exception');
    1546          $result = core_calendar_external::update_event_start_day($event->id, $newstartdate->getTimestamp());
    1547          $result = external_api::clean_returnvalue(
    1548              core_calendar_external::update_event_start_day_returns(),
    1549              $result
    1550          );
    1551      }
    1552  
    1553      /**
    1554       * Submit a request where the time duration until is earlier than the time
    1555       * start in order to get a validation error from the server.
    1556       */
    1557      public function test_submit_create_update_form_validation_error() {
    1558          $user = $this->getDataGenerator()->create_user();
    1559          $timestart = new DateTime();
    1560          $interval = new DateInterval("P1D"); // One day.
    1561          $timedurationuntil = new DateTime();
    1562          $timedurationuntil->sub($interval);
    1563          $formdata = [
    1564              'id' => 0,
    1565              'userid' => $user->id,
    1566              'modulename' => '',
    1567              'instance' => 0,
    1568              'visible' => 1,
    1569              'name' => 'Test',
    1570              'timestart' => [
    1571                  'day' => $timestart->format('j'),
    1572                  'month' => $timestart->format('n'),
    1573                  'year' => $timestart->format('Y'),
    1574                  'hour' => $timestart->format('G'),
    1575                  'minute' => 0,
    1576              ],
    1577              'eventtype' => 'user',
    1578              'description' => [
    1579                  'text' => '',
    1580                  'format' => 1,
    1581              ],
    1582              'location' => 'Test',
    1583              'duration' => 1,
    1584              'timedurationuntil' => [
    1585                  'day' => $timedurationuntil->format('j'),
    1586                  'month' => $timedurationuntil->format('n'),
    1587                  'year' => $timedurationuntil->format('Y'),
    1588                  'hour' => $timedurationuntil->format('G'),
    1589                  'minute' => 0,
    1590              ]
    1591          ];
    1592  
    1593          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1594  
    1595          $querystring = http_build_query($formdata, '', '&amp;');
    1596  
    1597          $this->resetAfterTest(true);
    1598          $this->setUser($user);
    1599  
    1600          $result = external_api::clean_returnvalue(
    1601              core_calendar_external::submit_create_update_form_returns(),
    1602              core_calendar_external::submit_create_update_form($querystring)
    1603          );
    1604  
    1605          $this->assertTrue($result['validationerror']);
    1606      }
    1607  
    1608      /**
    1609       * A user with the moodle/calendar:manageownentries capability at the
    1610       * system context should be able to create a user event.
    1611       */
    1612      public function test_submit_create_update_form_create_user_event() {
    1613          $generator = $this->getDataGenerator();
    1614          $user = $generator->create_user();
    1615          $roleid = $generator->create_role();
    1616          $context = \context_system::instance();
    1617          $timestart = new DateTime();
    1618          $interval = new DateInterval("P1D"); // One day.
    1619          $timedurationuntil = new DateTime();
    1620          $timedurationuntil->add($interval);
    1621          $formdata = [
    1622              'id' => 0,
    1623              'userid' => $user->id,
    1624              'modulename' => '',
    1625              'instance' => 0,
    1626              'visible' => 1,
    1627              'name' => 'Test',
    1628              'timestart' => [
    1629                  'day' => $timestart->format('j'),
    1630                  'month' => $timestart->format('n'),
    1631                  'year' => $timestart->format('Y'),
    1632                  'hour' => $timestart->format('G'),
    1633                  'minute' => 0,
    1634              ],
    1635              'eventtype' => 'user',
    1636              'description' => [
    1637                  'text' => '',
    1638                  'format' => 1,
    1639                  'itemid' => 0
    1640              ],
    1641              'location' => 'Test',
    1642              'duration' => 1,
    1643              'timedurationuntil' => [
    1644                  'day' => $timedurationuntil->format('j'),
    1645                  'month' => $timedurationuntil->format('n'),
    1646                  'year' => $timedurationuntil->format('Y'),
    1647                  'hour' => $timedurationuntil->format('G'),
    1648                  'minute' => 0,
    1649              ]
    1650          ];
    1651  
    1652          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1653          $querystring = http_build_query($formdata, '', '&');
    1654  
    1655          $generator->role_assign($roleid, $user->id, $context->id);
    1656          assign_capability('moodle/calendar:manageownentries', CAP_ALLOW, $roleid, $context, true);
    1657  
    1658          $user->ignoresesskey = true;
    1659          $this->resetAfterTest(true);
    1660          $this->setUser($user);
    1661  
    1662          $result = external_api::clean_returnvalue(
    1663              core_calendar_external::submit_create_update_form_returns(),
    1664              core_calendar_external::submit_create_update_form($querystring)
    1665          );
    1666  
    1667          $event = $result['event'];
    1668          $this->assertEquals($user->id, $event['userid']);
    1669          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    1670          $this->assertEquals($formdata['name'], $event['name']);
    1671      }
    1672  
    1673      /**
    1674       * A user without the moodle/calendar:manageownentries capability at the
    1675       * system context should not be able to create a user event.
    1676       */
    1677      public function test_submit_create_update_form_create_user_event_no_permission() {
    1678          $generator = $this->getDataGenerator();
    1679          $user = $generator->create_user();
    1680          $roleid = $generator->create_role();
    1681          $context = \context_system::instance();
    1682          $timestart = new DateTime();
    1683          $interval = new DateInterval("P1D"); // One day.
    1684          $timedurationuntil = new DateTime();
    1685          $timedurationuntil->add($interval);
    1686          $formdata = [
    1687              'id' => 0,
    1688              'userid' => $user->id,
    1689              'modulename' => '',
    1690              'instance' => 0,
    1691              'visible' => 1,
    1692              'name' => 'Test',
    1693              'timestart' => [
    1694                  'day' => $timestart->format('j'),
    1695                  'month' => $timestart->format('n'),
    1696                  'year' => $timestart->format('Y'),
    1697                  'hour' => $timestart->format('G'),
    1698                  'minute' => 0,
    1699              ],
    1700              'eventtype' => 'user',
    1701              'description' => [
    1702                  'text' => '',
    1703                  'format' => 1,
    1704              ],
    1705              'location' => 'Test',
    1706              'duration' => 1,
    1707              'timedurationuntil' => [
    1708                  'day' => $timedurationuntil->format('j'),
    1709                  'month' => $timedurationuntil->format('n'),
    1710                  'year' => $timedurationuntil->format('Y'),
    1711                  'hour' => $timedurationuntil->format('G'),
    1712                  'minute' => 0,
    1713              ]
    1714          ];
    1715  
    1716          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1717          $querystring = http_build_query($formdata, '', '&');
    1718  
    1719          $generator->role_assign($roleid, $user->id, $context->id);
    1720          assign_capability('moodle/calendar:manageownentries', CAP_PROHIBIT, $roleid, $context, true);
    1721  
    1722          $user->ignoresesskey = true;
    1723          $this->resetAfterTest(true);
    1724          $this->setUser($user);
    1725  
    1726          $this->expectException('moodle_exception');
    1727  
    1728          external_api::clean_returnvalue(
    1729              core_calendar_external::submit_create_update_form_returns(),
    1730              core_calendar_external::submit_create_update_form($querystring)
    1731          );
    1732      }
    1733  
    1734      /**
    1735       * A user with the moodle/calendar:manageentries capability at the
    1736       * site course context should be able to create a site event.
    1737       */
    1738      public function test_submit_create_update_form_create_site_event() {
    1739          $generator = $this->getDataGenerator();
    1740          $user = $generator->create_user();
    1741          $context = context_system::instance();
    1742          $roleid = $generator->create_role();
    1743          $timestart = new DateTime();
    1744          $interval = new DateInterval("P1D"); // One day.
    1745          $timedurationuntil = new DateTime();
    1746          $timedurationuntil->add($interval);
    1747          $formdata = [
    1748              'id' => 0,
    1749              'userid' => $user->id,
    1750              'modulename' => '',
    1751              'instance' => 0,
    1752              'visible' => 1,
    1753              'name' => 'Test',
    1754              'timestart' => [
    1755                  'day' => $timestart->format('j'),
    1756                  'month' => $timestart->format('n'),
    1757                  'year' => $timestart->format('Y'),
    1758                  'hour' => $timestart->format('G'),
    1759                  'minute' => 0,
    1760              ],
    1761              'eventtype' => 'site',
    1762              'description' => [
    1763                  'text' => '',
    1764                  'format' => 1,
    1765                  'itemid' => 0
    1766              ],
    1767              'location' => 'Test',
    1768              'duration' => 1,
    1769              'timedurationuntil' => [
    1770                  'day' => $timedurationuntil->format('j'),
    1771                  'month' => $timedurationuntil->format('n'),
    1772                  'year' => $timedurationuntil->format('Y'),
    1773                  'hour' => $timedurationuntil->format('G'),
    1774                  'minute' => 0,
    1775              ]
    1776          ];
    1777  
    1778          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1779          $querystring = http_build_query($formdata, '', '&');
    1780  
    1781          $generator->role_assign($roleid, $user->id, $context->id);
    1782  
    1783          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    1784  
    1785          $user->ignoresesskey = true;
    1786          $this->resetAfterTest(true);
    1787          $this->setUser($user);
    1788  
    1789          $result = external_api::clean_returnvalue(
    1790              core_calendar_external::submit_create_update_form_returns(),
    1791              core_calendar_external::submit_create_update_form($querystring)
    1792          );
    1793  
    1794          $event = $result['event'];
    1795          $this->assertEquals($user->id, $event['userid']);
    1796          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    1797          $this->assertEquals($formdata['name'], $event['name']);
    1798      }
    1799  
    1800      /**
    1801       * A user without the moodle/calendar:manageentries capability at the
    1802       * site course context should not be able to create a site event.
    1803       */
    1804      public function test_submit_create_update_form_create_site_event_no_permission() {
    1805          $generator = $this->getDataGenerator();
    1806          $user = $generator->create_user();
    1807          $context = context_course::instance(SITEID);
    1808          $roleid = $generator->create_role();
    1809          $timestart = new DateTime();
    1810          $interval = new DateInterval("P1D"); // One day.
    1811          $timedurationuntil = new DateTime();
    1812          $timedurationuntil->add($interval);
    1813          $formdata = [
    1814              'id' => 0,
    1815              'userid' => $user->id,
    1816              'modulename' => '',
    1817              'instance' => 0,
    1818              'visible' => 1,
    1819              'name' => 'Test',
    1820              'timestart' => [
    1821                  'day' => $timestart->format('j'),
    1822                  'month' => $timestart->format('n'),
    1823                  'year' => $timestart->format('Y'),
    1824                  'hour' => $timestart->format('G'),
    1825                  'minute' => 0,
    1826              ],
    1827              'eventtype' => 'site',
    1828              'description' => [
    1829                  'text' => '',
    1830                  'format' => 1,
    1831              ],
    1832              'location' => 'Test',
    1833              'duration' => 1,
    1834              'timedurationuntil' => [
    1835                  'day' => $timedurationuntil->format('j'),
    1836                  'month' => $timedurationuntil->format('n'),
    1837                  'year' => $timedurationuntil->format('Y'),
    1838                  'hour' => $timedurationuntil->format('G'),
    1839                  'minute' => 0,
    1840              ]
    1841          ];
    1842  
    1843          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1844          $querystring = http_build_query($formdata, '', '&');
    1845  
    1846          $generator->role_assign($roleid, $user->id, $context->id);
    1847  
    1848          assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
    1849  
    1850          $user->ignoresesskey = true;
    1851          $this->resetAfterTest(true);
    1852          $this->setUser($user);
    1853  
    1854          $result = external_api::clean_returnvalue(
    1855              core_calendar_external::submit_create_update_form_returns(),
    1856              core_calendar_external::submit_create_update_form($querystring)
    1857          );
    1858  
    1859          $this->assertTrue($result['validationerror']);
    1860      }
    1861  
    1862      /**
    1863       * A user that has the moodle/calendar:manageentries in a course that they
    1864       * are enrolled in should be able to create a course event in that course.
    1865       */
    1866      public function test_submit_create_update_form_create_course_event() {
    1867          $generator = $this->getDataGenerator();
    1868          $user = $generator->create_user();
    1869          $course = $generator->create_course();
    1870          $context = context_course::instance($course->id);
    1871          $roleid = $generator->create_role();
    1872          $timestart = new DateTime();
    1873          $interval = new DateInterval("P1D"); // One day.
    1874          $timedurationuntil = new DateTime();
    1875          $timedurationuntil->add($interval);
    1876          $formdata = [
    1877              'id' => 0,
    1878              'userid' => $user->id,
    1879              'modulename' => '',
    1880              'instance' => 0,
    1881              'visible' => 1,
    1882              'name' => 'Test',
    1883              'timestart' => [
    1884                  'day' => $timestart->format('j'),
    1885                  'month' => $timestart->format('n'),
    1886                  'year' => $timestart->format('Y'),
    1887                  'hour' => $timestart->format('G'),
    1888                  'minute' => 0,
    1889              ],
    1890              'eventtype' => 'course',
    1891              'courseid' => $course->id,
    1892              'description' => [
    1893                  'text' => '',
    1894                  'format' => 1,
    1895                  'itemid' => 0,
    1896              ],
    1897              'location' => 'Test',
    1898              'duration' => 1,
    1899              'timedurationuntil' => [
    1900                  'day' => $timedurationuntil->format('j'),
    1901                  'month' => $timedurationuntil->format('n'),
    1902                  'year' => $timedurationuntil->format('Y'),
    1903                  'hour' => $timedurationuntil->format('G'),
    1904                  'minute' => 0,
    1905              ]
    1906          ];
    1907  
    1908          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1909          $querystring = http_build_query($formdata, '', '&');
    1910  
    1911          $generator->enrol_user($user->id, $course->id, 'student');
    1912          $generator->role_assign($roleid, $user->id, $context->id);
    1913  
    1914          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    1915  
    1916          $user->ignoresesskey = true;
    1917          $this->resetAfterTest(true);
    1918          $this->setUser($user);
    1919  
    1920          $result = external_api::clean_returnvalue(
    1921              core_calendar_external::submit_create_update_form_returns(),
    1922              core_calendar_external::submit_create_update_form($querystring)
    1923          );
    1924  
    1925          $event = $result['event'];
    1926          $this->assertEquals($user->id, $event['userid']);
    1927          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    1928          $this->assertEquals($formdata['name'], $event['name']);
    1929          $this->assertEquals($formdata['courseid'], $event['course']['id']);
    1930      }
    1931  
    1932      /**
    1933       * A user without the moodle/calendar:manageentries capability in a course
    1934       * that they are enrolled in should not be able to create a course event in that course.
    1935       */
    1936      public function test_submit_create_update_form_create_course_event_no_permission() {
    1937          $generator = $this->getDataGenerator();
    1938          $user = $generator->create_user();
    1939          $course = $generator->create_course();
    1940          $context = context_course::instance($course->id);
    1941          $roleid = $generator->create_role();
    1942          $timestart = new DateTime();
    1943          $interval = new DateInterval("P1D"); // One day.
    1944          $timedurationuntil = new DateTime();
    1945          $timedurationuntil->add($interval);
    1946          $formdata = [
    1947              'id' => 0,
    1948              'userid' => $user->id,
    1949              'modulename' => '',
    1950              'instance' => 0,
    1951              'visible' => 1,
    1952              'name' => 'Test',
    1953              'timestart' => [
    1954                  'day' => $timestart->format('j'),
    1955                  'month' => $timestart->format('n'),
    1956                  'year' => $timestart->format('Y'),
    1957                  'hour' => $timestart->format('G'),
    1958                  'minute' => 0,
    1959              ],
    1960              'eventtype' => 'course',
    1961              'courseid' => $course->id,
    1962              'description' => [
    1963                  'text' => '',
    1964                  'format' => 1,
    1965              ],
    1966              'location' => 'Test',
    1967              'duration' => 1,
    1968              'timedurationuntil' => [
    1969                  'day' => $timedurationuntil->format('j'),
    1970                  'month' => $timedurationuntil->format('n'),
    1971                  'year' => $timedurationuntil->format('Y'),
    1972                  'hour' => $timedurationuntil->format('G'),
    1973                  'minute' => 0,
    1974              ]
    1975          ];
    1976  
    1977          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    1978          $querystring = http_build_query($formdata, '', '&');
    1979  
    1980          $generator->enrol_user($user->id, $course->id, 'student');
    1981          $generator->role_assign($roleid, $user->id, $context->id);
    1982  
    1983          assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
    1984  
    1985          $user->ignoresesskey = true;
    1986          $this->resetAfterTest(true);
    1987          $this->setUser($user);
    1988  
    1989          $result = external_api::clean_returnvalue(
    1990              core_calendar_external::submit_create_update_form_returns(),
    1991              core_calendar_external::submit_create_update_form($querystring)
    1992          );
    1993  
    1994          $this->assertTrue($result['validationerror']);
    1995      }
    1996  
    1997      /**
    1998       * A user should not be able to create an event for a course that they are
    1999       * not enrolled in.
    2000       */
    2001      public function test_submit_create_update_form_create_course_event_not_enrolled() {
    2002          $generator = $this->getDataGenerator();
    2003          $user = $generator->create_user();
    2004          $course = $generator->create_course();
    2005          $course2 = $generator->create_course();
    2006          $context = context_course::instance($course->id);
    2007          $roleid = $generator->create_role();
    2008          $timestart = new DateTime();
    2009          $interval = new DateInterval("P1D"); // One day.
    2010          $timedurationuntil = new DateTime();
    2011          $timedurationuntil->add($interval);
    2012          $formdata = [
    2013              'id' => 0,
    2014              'userid' => $user->id,
    2015              'modulename' => '',
    2016              'instance' => 0,
    2017              'visible' => 1,
    2018              'name' => 'Test',
    2019              'timestart' => [
    2020                  'day' => $timestart->format('j'),
    2021                  'month' => $timestart->format('n'),
    2022                  'year' => $timestart->format('Y'),
    2023                  'hour' => $timestart->format('G'),
    2024                  'minute' => 0,
    2025              ],
    2026              'eventtype' => 'course',
    2027              'courseid' => $course2->id, // Not enrolled.
    2028              'description' => [
    2029                  'text' => '',
    2030                  'format' => 1,
    2031              ],
    2032              'location' => 'Test',
    2033              'duration' => 1,
    2034              'timedurationuntil' => [
    2035                  'day' => $timedurationuntil->format('j'),
    2036                  'month' => $timedurationuntil->format('n'),
    2037                  'year' => $timedurationuntil->format('Y'),
    2038                  'hour' => $timedurationuntil->format('G'),
    2039                  'minute' => 0,
    2040              ]
    2041          ];
    2042  
    2043          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    2044          $querystring = http_build_query($formdata, '', '&');
    2045  
    2046          $generator->enrol_user($user->id, $course->id, 'student');
    2047          $generator->role_assign($roleid, $user->id, $context->id);
    2048  
    2049          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    2050  
    2051          $user->ignoresesskey = true;
    2052          $this->resetAfterTest(true);
    2053          $this->setUser($user);
    2054  
    2055          $result = external_api::clean_returnvalue(
    2056              core_calendar_external::submit_create_update_form_returns(),
    2057              core_calendar_external::submit_create_update_form($querystring)
    2058          );
    2059  
    2060          $this->assertTrue($result['validationerror']);
    2061      }
    2062  
    2063      /**
    2064       * A user should be able to create an event for a group that they are a member of in
    2065       * a course in which they are enrolled and have the moodle/calendar:manageentries capability.
    2066       */
    2067      public function test_submit_create_update_form_create_group_event_group_member_manage_course() {
    2068          $generator = $this->getDataGenerator();
    2069          $user = $generator->create_user();
    2070          $course = $generator->create_course();
    2071          $group = $generator->create_group(array('courseid' => $course->id));
    2072          $context = context_course::instance($course->id);
    2073          $roleid = $generator->create_role();
    2074          $timestart = new DateTime();
    2075          $interval = new DateInterval("P1D"); // One day.
    2076          $timedurationuntil = new DateTime();
    2077          $timedurationuntil->add($interval);
    2078          $formdata = [
    2079              'id' => 0,
    2080              'userid' => $user->id,
    2081              'modulename' => '',
    2082              'instance' => 0,
    2083              'visible' => 1,
    2084              'name' => 'Test',
    2085              'timestart' => [
    2086                  'day' => $timestart->format('j'),
    2087                  'month' => $timestart->format('n'),
    2088                  'year' => $timestart->format('Y'),
    2089                  'hour' => $timestart->format('G'),
    2090                  'minute' => 0,
    2091              ],
    2092              'eventtype' => 'group',
    2093              'groupid' => $group->id,
    2094              'groupcourseid' => $course->id,
    2095              'description' => [
    2096                  'text' => '',
    2097                  'format' => 1,
    2098                  'itemid' => 0
    2099              ],
    2100              'location' => 'Test',
    2101              'duration' => 1,
    2102              'timedurationuntil' => [
    2103                  'day' => $timedurationuntil->format('j'),
    2104                  'month' => $timedurationuntil->format('n'),
    2105                  'year' => $timedurationuntil->format('Y'),
    2106                  'hour' => $timedurationuntil->format('G'),
    2107                  'minute' => 0,
    2108              ]
    2109          ];
    2110  
    2111          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    2112          $querystring = http_build_query($formdata, '', '&');
    2113  
    2114          $generator->enrol_user($user->id, $course->id, 'student');
    2115          $generator->role_assign($roleid, $user->id, $context->id);
    2116          $generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
    2117  
    2118          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    2119  
    2120          $user->ignoresesskey = true;
    2121          $this->resetAfterTest(true);
    2122          $this->setUser($user);
    2123  
    2124          $result = external_api::clean_returnvalue(
    2125              core_calendar_external::submit_create_update_form_returns(),
    2126              core_calendar_external::submit_create_update_form($querystring)
    2127          );
    2128  
    2129          $event = $result['event'];
    2130          $this->assertEquals($user->id, $event['userid']);
    2131          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    2132          $this->assertEquals($formdata['name'], $event['name']);
    2133          $this->assertEquals($group->id, $event['groupid']);
    2134      }
    2135  
    2136      /**
    2137       * A user should be able to create an event for a group that they are a member of in
    2138       * a course in which they are enrolled and have the moodle/calendar:managegroupentries capability.
    2139       */
    2140      public function test_submit_create_update_form_create_group_event_group_member_manage_group_entries() {
    2141          $generator = $this->getDataGenerator();
    2142          $user = $generator->create_user();
    2143          $course = $generator->create_course();
    2144          $group = $generator->create_group(array('courseid' => $course->id));
    2145          $context = context_course::instance($course->id);
    2146          $roleid = $generator->create_role();
    2147          $timestart = new DateTime();
    2148          $interval = new DateInterval("P1D"); // One day.
    2149          $timedurationuntil = new DateTime();
    2150          $timedurationuntil->add($interval);
    2151          $formdata = [
    2152              'id' => 0,
    2153              'userid' => $user->id,
    2154              'modulename' => '',
    2155              'instance' => 0,
    2156              'visible' => 1,
    2157              'name' => 'Test',
    2158              'timestart' => [
    2159                  'day' => $timestart->format('j'),
    2160                  'month' => $timestart->format('n'),
    2161                  'year' => $timestart->format('Y'),
    2162                  'hour' => $timestart->format('G'),
    2163                  'minute' => 0,
    2164              ],
    2165              'eventtype' => 'group',
    2166              'groupid' => $group->id,
    2167              'groupcourseid' => $course->id,
    2168              'description' => [
    2169                  'text' => '',
    2170                  'format' => 1,
    2171                  'itemid' => 0
    2172              ],
    2173              'location' => 'Test',
    2174              'duration' => 1,
    2175              'timedurationuntil' => [
    2176                  'day' => $timedurationuntil->format('j'),
    2177                  'month' => $timedurationuntil->format('n'),
    2178                  'year' => $timedurationuntil->format('Y'),
    2179                  'hour' => $timedurationuntil->format('G'),
    2180                  'minute' => 0,
    2181              ]
    2182          ];
    2183  
    2184          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    2185          $querystring = http_build_query($formdata, '', '&');
    2186  
    2187          $generator->enrol_user($user->id, $course->id, 'student');
    2188          $generator->role_assign($roleid, $user->id, $context->id);
    2189          $generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
    2190  
    2191          assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
    2192          assign_capability('moodle/calendar:managegroupentries', CAP_ALLOW, $roleid, $context, true);
    2193  
    2194          $user->ignoresesskey = true;
    2195          $this->resetAfterTest(true);
    2196          $this->setUser($user);
    2197  
    2198          $result = external_api::clean_returnvalue(
    2199              core_calendar_external::submit_create_update_form_returns(),
    2200              core_calendar_external::submit_create_update_form($querystring)
    2201          );
    2202  
    2203          $event = $result['event'];
    2204          $this->assertEquals($user->id, $event['userid']);
    2205          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    2206          $this->assertEquals($formdata['name'], $event['name']);
    2207          $this->assertEquals($group->id, $event['groupid']);
    2208      }
    2209  
    2210      /**
    2211       * A user should be able to create an event for any group in a course in which
    2212       * they are enrolled and have the moodle/site:accessallgroups capability.
    2213       */
    2214      public function test_submit_create_update_form_create_group_event_access_all_groups() {
    2215          $generator = $this->getDataGenerator();
    2216          $user = $generator->create_user();
    2217          $course = $generator->create_course();
    2218          $group = $generator->create_group(array('courseid' => $course->id));
    2219          $context = context_course::instance($course->id);
    2220          $roleid = $generator->create_role();
    2221          $timestart = new DateTime();
    2222          $interval = new DateInterval("P1D"); // One day.
    2223          $timedurationuntil = new DateTime();
    2224          $timedurationuntil->add($interval);
    2225          $formdata = [
    2226              'id' => 0,
    2227              'userid' => $user->id,
    2228              'modulename' => '',
    2229              'instance' => 0,
    2230              'visible' => 1,
    2231              'name' => 'Test',
    2232              'timestart' => [
    2233                  'day' => $timestart->format('j'),
    2234                  'month' => $timestart->format('n'),
    2235                  'year' => $timestart->format('Y'),
    2236                  'hour' => $timestart->format('G'),
    2237                  'minute' => 0,
    2238              ],
    2239              'eventtype' => 'group',
    2240              'groupid' => $group->id,
    2241              'groupcourseid' => $course->id,
    2242              'description' => [
    2243                  'text' => '',
    2244                  'format' => 1,
    2245                  'itemid' => 0
    2246              ],
    2247              'location' => 'Test',
    2248              'duration' => 1,
    2249              'timedurationuntil' => [
    2250                  'day' => $timedurationuntil->format('j'),
    2251                  'month' => $timedurationuntil->format('n'),
    2252                  'year' => $timedurationuntil->format('Y'),
    2253                  'hour' => $timedurationuntil->format('G'),
    2254                  'minute' => 0,
    2255              ]
    2256          ];
    2257  
    2258          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    2259          $querystring = http_build_query($formdata, '', '&');
    2260  
    2261          $generator->enrol_user($user->id, $course->id, 'student');
    2262          $generator->role_assign($roleid, $user->id, $context->id);
    2263  
    2264          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    2265          assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $context, true);
    2266  
    2267          $user->ignoresesskey = true;
    2268          $this->resetAfterTest(true);
    2269          $this->setUser($user);
    2270  
    2271          $result = external_api::clean_returnvalue(
    2272              core_calendar_external::submit_create_update_form_returns(),
    2273              core_calendar_external::submit_create_update_form($querystring)
    2274          );
    2275  
    2276          $event = $result['event'];
    2277          $this->assertEquals($user->id, $event['userid']);
    2278          $this->assertEquals($formdata['eventtype'], $event['eventtype']);
    2279          $this->assertEquals($formdata['name'], $event['name']);
    2280          $this->assertEquals($group->id, $event['groupid']);
    2281      }
    2282  
    2283      /**
    2284       * A user should not be able to create an event for any group that they are not a
    2285       * member of in a course in which they are enrolled but don't have the
    2286       * moodle/site:accessallgroups capability.
    2287       */
    2288      public function test_submit_create_update_form_create_group_event_non_member_no_permission() {
    2289          $generator = $this->getDataGenerator();
    2290          $user = $generator->create_user();
    2291          $course = $generator->create_course();
    2292          $group = $generator->create_group(array('courseid' => $course->id));
    2293          $context = context_course::instance($course->id);
    2294          $roleid = $generator->create_role();
    2295          $timestart = new DateTime();
    2296          $interval = new DateInterval("P1D"); // One day.
    2297          $timedurationuntil = new DateTime();
    2298          $timedurationuntil->add($interval);
    2299          $formdata = [
    2300              'id' => 0,
    2301              'userid' => $user->id,
    2302              'modulename' => '',
    2303              'instance' => 0,
    2304              'visible' => 1,
    2305              'name' => 'Test',
    2306              'timestart' => [
    2307                  'day' => $timestart->format('j'),
    2308                  'month' => $timestart->format('n'),
    2309                  'year' => $timestart->format('Y'),
    2310                  'hour' => $timestart->format('G'),
    2311                  'minute' => 0,
    2312              ],
    2313              'eventtype' => 'group',
    2314              'groupid' => $group->id,
    2315              'groupcourseid' => $course->id,
    2316              'description' => [
    2317                  'text' => '',
    2318                  'format' => 1,
    2319              ],
    2320              'location' => 'Test',
    2321              'duration' => 1,
    2322              'timedurationuntil' => [
    2323                  'day' => $timedurationuntil->format('j'),
    2324                  'month' => $timedurationuntil->format('n'),
    2325                  'year' => $timedurationuntil->format('Y'),
    2326                  'hour' => $timedurationuntil->format('G'),
    2327                  'minute' => 0,
    2328              ]
    2329          ];
    2330  
    2331          $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata);
    2332          $querystring = http_build_query($formdata, '', '&');
    2333  
    2334          $generator->enrol_user($user->id, $course->id, 'student');
    2335          $generator->role_assign($roleid, $user->id, $context->id);
    2336  
    2337          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
    2338          assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $roleid, $context, true);
    2339  
    2340          $user->ignoresesskey = true;
    2341          $this->resetAfterTest(true);
    2342          $this->setUser($user);
    2343  
    2344          $result = external_api::clean_returnvalue(
    2345              core_calendar_external::submit_create_update_form_returns(),
    2346              core_calendar_external::submit_create_update_form($querystring)
    2347          );
    2348  
    2349          $this->assertTrue($result['validationerror']);
    2350      }
    2351  
    2352      /**
    2353       * A user should not be able load the calendar monthly view for a course they cannot access.
    2354       */
    2355      public function test_get_calendar_monthly_view_no_course_permission() {
    2356          global $USER;
    2357          $this->resetAfterTest(true);
    2358          $this->setAdminUser();
    2359  
    2360          $generator = $this->getDataGenerator();
    2361          $user1 = $generator->create_user();
    2362          $user2 = $generator->create_user();
    2363          $course = $generator->create_course();
    2364          $generator->enrol_user($user1->id, $course->id, 'student');
    2365          $name = 'Course Event (course' . $course->id . ')';
    2366          $record = new stdClass();
    2367          $record->courseid = $course->id;
    2368          $courseevent = $this->create_calendar_event($name, $USER->id, 'course', 0, time(), $record);
    2369  
    2370          $timestart = new DateTime();
    2371          // Admin can load the course.
    2372          $data = external_api::clean_returnvalue(
    2373              core_calendar_external::get_calendar_monthly_view_returns(),
    2374              core_calendar_external::get_calendar_monthly_view($timestart->format('Y'), $timestart->format('n'),
    2375                                                                $course->id, null, false, true)
    2376          );
    2377          $this->assertEquals($data['courseid'], $course->id);
    2378          // User enrolled in the course can load the course calendar.
    2379          $this->setUser($user1);
    2380          $data = external_api::clean_returnvalue(
    2381              core_calendar_external::get_calendar_monthly_view_returns(),
    2382              core_calendar_external::get_calendar_monthly_view($timestart->format('Y'), $timestart->format('n'),
    2383                                                                $course->id, null, false, true)
    2384          );
    2385          $this->assertEquals($data['courseid'], $course->id);
    2386          // User not enrolled in the course cannot load the course calendar.
    2387          $this->setUser($user2);
    2388          $this->expectException('require_login_exception');
    2389          $data = external_api::clean_returnvalue(
    2390              core_calendar_external::get_calendar_monthly_view_returns(),
    2391              core_calendar_external::get_calendar_monthly_view($timestart->format('Y'), $timestart->format('n'),
    2392                                                                $course->id, null, false, false)
    2393          );
    2394      }
    2395  
    2396      /**
    2397       * A user should not be able load the calendar day view for a course they cannot access.
    2398       */
    2399      public function test_get_calendar_day_view_no_course_permission() {
    2400          global $USER;
    2401          $this->resetAfterTest(true);
    2402          $this->setAdminUser();
    2403  
    2404          $generator = $this->getDataGenerator();
    2405          $user1 = $generator->create_user();
    2406          $user2 = $generator->create_user();
    2407          $course = $generator->create_course();
    2408          $generator->enrol_user($user1->id, $course->id, 'student');
    2409          $name = 'Course Event (course' . $course->id . ')';
    2410          $record = new stdClass();
    2411          $record->courseid = $course->id;
    2412          $courseevent = $this->create_calendar_event($name, $USER->id, 'course', 0, time(), $record);
    2413  
    2414          $timestart = new DateTime();
    2415          // Admin can load the course.
    2416          $data = external_api::clean_returnvalue(
    2417              core_calendar_external::get_calendar_day_view_returns(),
    2418              core_calendar_external::get_calendar_day_view($timestart->format('Y'), $timestart->format('n'),
    2419                                                            $timestart->format('j'), $course->id, null)
    2420          );
    2421          $this->assertEquals($data['courseid'], $course->id);
    2422          // User enrolled in the course can load the course calendar.
    2423          $this->setUser($user1);
    2424          $data = external_api::clean_returnvalue(
    2425              core_calendar_external::get_calendar_day_view_returns(),
    2426              core_calendar_external::get_calendar_day_view($timestart->format('Y'), $timestart->format('n'),
    2427                                                            $timestart->format('j'), $course->id, null)
    2428          );
    2429          $this->assertEquals($data['courseid'], $course->id);
    2430          // User not enrolled in the course cannot load the course calendar.
    2431          $this->setUser($user2);
    2432          $this->expectException('require_login_exception');
    2433          $data = external_api::clean_returnvalue(
    2434              core_calendar_external::get_calendar_day_view_returns(),
    2435              core_calendar_external::get_calendar_day_view($timestart->format('Y'), $timestart->format('n'),
    2436                                                            $timestart->format('j'), $course->id, null)
    2437          );
    2438      }
    2439  
    2440      /**
    2441       * A user should not be able load the calendar upcoming view for a course they cannot access.
    2442       */
    2443      public function test_get_calendar_upcoming_view_no_course_permission() {
    2444          global $USER;
    2445          $this->resetAfterTest(true);
    2446          $this->setAdminUser();
    2447  
    2448          $generator = $this->getDataGenerator();
    2449          $user1 = $generator->create_user();
    2450          $user2 = $generator->create_user();
    2451          $course = $generator->create_course();
    2452          $generator->enrol_user($user1->id, $course->id, 'student');
    2453          $name = 'Course Event (course' . $course->id . ')';
    2454          $record = new stdClass();
    2455          $record->courseid = $course->id;
    2456          $courseevent = $this->create_calendar_event($name, $USER->id, 'course', 0, time(), $record);
    2457  
    2458          // Admin can load the course.
    2459          $data = external_api::clean_returnvalue(
    2460              core_calendar_external::get_calendar_upcoming_view_returns(),
    2461              core_calendar_external::get_calendar_upcoming_view($course->id, null)
    2462          );
    2463          $this->assertEquals($data['courseid'], $course->id);
    2464          // User enrolled in the course can load the course calendar.
    2465          $this->setUser($user1);
    2466          $data = external_api::clean_returnvalue(
    2467              core_calendar_external::get_calendar_upcoming_view_returns(),
    2468              core_calendar_external::get_calendar_upcoming_view($course->id, null)
    2469          );
    2470          $this->assertEquals($data['courseid'], $course->id);
    2471          // User not enrolled in the course cannot load the course calendar.
    2472          $this->setUser($user2);
    2473          $this->expectException('require_login_exception');
    2474          $data = external_api::clean_returnvalue(
    2475              core_calendar_external::get_calendar_upcoming_view_returns(),
    2476              core_calendar_external::get_calendar_upcoming_view($course->id, null)
    2477          );
    2478      }
    2479  
    2480      /**
    2481       * A user should not be able load the calendar event for a course they cannot access.
    2482       */
    2483      public function test_get_calendar_event_by_id_no_course_permission() {
    2484          global $USER;
    2485          $this->resetAfterTest(true);
    2486          $this->setAdminUser();
    2487  
    2488          $generator = $this->getDataGenerator();
    2489          $user1 = $generator->create_user();
    2490          $user2 = $generator->create_user();
    2491          $course = $generator->create_course();
    2492          $generator->enrol_user($user1->id, $course->id, 'student');
    2493          $name = 'Course Event (course' . $course->id . ')';
    2494          $record = new stdClass();
    2495          $record->courseid = $course->id;
    2496          $courseevent = $this->create_calendar_event($name, $USER->id, 'course', 0, time(), $record);
    2497  
    2498          // Admin can load the course event.
    2499          $data = external_api::clean_returnvalue(
    2500              core_calendar_external::get_calendar_event_by_id_returns(),
    2501              core_calendar_external::get_calendar_event_by_id($courseevent->id)
    2502          );
    2503          $this->assertEquals($data['event']['id'], $courseevent->id);
    2504          // User enrolled in the course can load the course event.
    2505          $this->setUser($user1);
    2506          $data = external_api::clean_returnvalue(
    2507              core_calendar_external::get_calendar_event_by_id_returns(),
    2508              core_calendar_external::get_calendar_event_by_id($courseevent->id)
    2509          );
    2510          $this->assertEquals($data['event']['id'], $courseevent->id);
    2511          // User not enrolled in the course cannot load the course event.
    2512          $this->setUser($user2);
    2513          $this->expectException('required_capability_exception');
    2514          $data = external_api::clean_returnvalue(
    2515              core_calendar_external::get_calendar_event_by_id_returns(),
    2516              core_calendar_external::get_calendar_event_by_id($courseevent->id)
    2517          );
    2518      }
    2519  
    2520      /**
    2521       * A user should not be able load the calendar events for a category they cannot see.
    2522       */
    2523      public function test_get_calendar_events_hidden_category() {
    2524          global $USER;
    2525          $this->resetAfterTest(true);
    2526          $this->setAdminUser();
    2527  
    2528          $generator = $this->getDataGenerator();
    2529          $user1 = $generator->create_user();
    2530          $category = $generator->create_category(['visible' => 0]);
    2531          $name = 'Category Event (category: ' . $category->id . ')';
    2532          $record = new stdClass();
    2533          $record->categoryid = $category->id;
    2534          $categoryevent = $this->create_calendar_event($name, $USER->id, 'category', 0, time(), $record);
    2535  
    2536          $events = [
    2537              'eventids' => [$categoryevent->id]
    2538          ];
    2539          $options = [];
    2540          // Admin can load the category event.
    2541          $data = external_api::clean_returnvalue(
    2542              core_calendar_external::get_calendar_events_returns(),
    2543              core_calendar_external::get_calendar_events($events, $options)
    2544          );
    2545          $this->assertEquals($data['events'][0]['id'], $categoryevent->id);
    2546          // User with no special permission to see hidden categories will not see the event.
    2547          $this->setUser($user1);
    2548          $data = external_api::clean_returnvalue(
    2549              core_calendar_external::get_calendar_events_returns(),
    2550              core_calendar_external::get_calendar_events($events, $options)
    2551          );
    2552          $this->assertCount(0, $data['events']);
    2553          $this->assertEquals('nopermissions', $data['warnings'][0]['warningcode']);
    2554      }
    2555  
    2556      /**
    2557       * Test get_calendar_access_information for admins.
    2558       */
    2559      public function test_get_calendar_access_information_for_admins() {
    2560          global $CFG;
    2561          $this->resetAfterTest(true);
    2562          $this->setAdminUser();
    2563  
    2564          $CFG->calendar_adminseesall = 1;
    2565  
    2566          $data = external_api::clean_returnvalue(
    2567              core_calendar_external::get_calendar_access_information_returns(),
    2568              core_calendar_external::get_calendar_access_information()
    2569          );
    2570          $this->assertTrue($data['canmanageownentries']);
    2571          $this->assertTrue($data['canmanagegroupentries']);
    2572          $this->assertTrue($data['canmanageentries']);
    2573      }
    2574  
    2575      /**
    2576       * Test get_calendar_access_information for authenticated users.
    2577       */
    2578      public function test_get_calendar_access_information_for_authenticated_users() {
    2579          $this->resetAfterTest(true);
    2580          $this->setUser($this->getDataGenerator()->create_user());
    2581  
    2582          $data = external_api::clean_returnvalue(
    2583              core_calendar_external::get_calendar_access_information_returns(),
    2584              core_calendar_external::get_calendar_access_information()
    2585          );
    2586          $this->assertTrue($data['canmanageownentries']);
    2587          $this->assertFalse($data['canmanagegroupentries']);
    2588          $this->assertFalse($data['canmanageentries']);
    2589      }
    2590  
    2591      /**
    2592       * Test get_calendar_access_information for student users.
    2593       */
    2594      public function test_get_calendar_access_information_for_student_users() {
    2595          global $DB;
    2596          $this->resetAfterTest(true);
    2597  
    2598          $user = $this->getDataGenerator()->create_user();
    2599          $course = $this->getDataGenerator()->create_course();
    2600          $role = $DB->get_record('role', array('shortname' => 'student'));
    2601          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
    2602  
    2603          $this->setUser($user);
    2604  
    2605          $data = external_api::clean_returnvalue(
    2606              core_calendar_external::get_calendar_access_information_returns(),
    2607              core_calendar_external::get_calendar_access_information($course->id)
    2608          );
    2609          $this->assertTrue($data['canmanageownentries']);
    2610          $this->assertFalse($data['canmanagegroupentries']);
    2611          $this->assertFalse($data['canmanageentries']);
    2612      }
    2613  
    2614      /**
    2615       * Test get_calendar_access_information for teacher users.
    2616       */
    2617      public function test_get_calendar_access_information_for_teacher_users() {
    2618          global $DB;
    2619          $this->resetAfterTest(true);
    2620  
    2621          $user = $this->getDataGenerator()->create_user();
    2622          $course = $this->getDataGenerator()->create_course(['groupmode' => 1]);
    2623          $role = $DB->get_record('role', array('shortname' => 'editingteacher'));
    2624          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
    2625          $this->getDataGenerator()->create_group(['courseid' => $course->id]);
    2626  
    2627          $this->setUser($user);
    2628  
    2629          $data = external_api::clean_returnvalue(
    2630              core_calendar_external::get_calendar_access_information_returns(),
    2631              core_calendar_external::get_calendar_access_information($course->id)
    2632          );
    2633          $this->assertTrue($data['canmanageownentries']);
    2634          $this->assertTrue($data['canmanagegroupentries']);
    2635          $this->assertTrue($data['canmanageentries']);
    2636      }
    2637  
    2638      /**
    2639       * Test get_allowed_event_types for admins.
    2640       */
    2641      public function test_get_allowed_event_types_for_admins() {
    2642          global $CFG;
    2643          $this->resetAfterTest(true);
    2644          $this->setAdminUser();
    2645          $CFG->calendar_adminseesall = 1;
    2646          $data = external_api::clean_returnvalue(
    2647              core_calendar_external::get_allowed_event_types_returns(),
    2648              core_calendar_external::get_allowed_event_types()
    2649          );
    2650          $this->assertEquals(['user', 'site', 'course', 'category'], $data['allowedeventtypes']);
    2651      }
    2652      /**
    2653       * Test get_allowed_event_types for authenticated users.
    2654       */
    2655      public function test_get_allowed_event_types_for_authenticated_users() {
    2656          $this->resetAfterTest(true);
    2657          $this->setUser($this->getDataGenerator()->create_user());
    2658          $data = external_api::clean_returnvalue(
    2659              core_calendar_external::get_allowed_event_types_returns(),
    2660              core_calendar_external::get_allowed_event_types()
    2661          );
    2662          $this->assertEquals(['user'], $data['allowedeventtypes']);
    2663      }
    2664      /**
    2665       * Test get_allowed_event_types for student users.
    2666       */
    2667      public function test_get_allowed_event_types_for_student_users() {
    2668          global $DB;
    2669          $this->resetAfterTest(true);
    2670          $user = $this->getDataGenerator()->create_user();
    2671          $course = $this->getDataGenerator()->create_course();
    2672          $role = $DB->get_record('role', array('shortname' => 'student'));
    2673          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
    2674          $this->setUser($user);
    2675          $data = external_api::clean_returnvalue(
    2676              core_calendar_external::get_allowed_event_types_returns(),
    2677              core_calendar_external::get_allowed_event_types($course->id)
    2678          );
    2679          $this->assertEquals(['user'], $data['allowedeventtypes']);
    2680      }
    2681      /**
    2682       * Test get_allowed_event_types for teacher users.
    2683       */
    2684      public function test_get_allowed_event_types_for_teacher_users() {
    2685          global $DB;
    2686          $this->resetAfterTest(true);
    2687          $user = $this->getDataGenerator()->create_user();
    2688          $course = $this->getDataGenerator()->create_course(['groupmode' => 1]);
    2689          $role = $DB->get_record('role', array('shortname' => 'editingteacher'));
    2690          $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
    2691          $this->getDataGenerator()->create_group(['courseid' =>