Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

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