Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

   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 API
  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   * @since Moodle 2.5
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  require_once($CFG->dirroot . '/calendar/lib.php');
  30  
  31  use core_calendar\local\api as local_api;
  32  use core_calendar\local\event\container as event_container;
  33  use core_calendar\local\event\forms\create as create_event_form;
  34  use core_calendar\local\event\forms\update as update_event_form;
  35  use core_calendar\local\event\mappers\create_update_form_mapper;
  36  use core_calendar\external\event_exporter;
  37  use core_calendar\external\events_exporter;
  38  use core_calendar\external\events_grouped_by_course_exporter;
  39  use core_calendar\external\events_related_objects_cache;
  40  use core_external\external_api;
  41  use core_external\external_format_value;
  42  use core_external\external_function_parameters;
  43  use core_external\external_multiple_structure;
  44  use core_external\external_single_structure;
  45  use core_external\external_value;
  46  use core_external\external_warnings;
  47  use core_external\util as external_util;
  48  
  49  /**
  50   * Calendar external functions
  51   *
  52   * @package    core_calendar
  53   * @category   external
  54   * @copyright  2012 Ankit Agarwal
  55   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  56   * @since Moodle 2.5
  57   */
  58  class core_calendar_external extends external_api {
  59      /**
  60       * Returns description of method parameters
  61       *
  62       * @return external_function_parameters
  63       * @since Moodle 2.5
  64       */
  65      public static function delete_calendar_events_parameters() {
  66          return new external_function_parameters(
  67                  array('events' => new external_multiple_structure(
  68                          new external_single_structure(
  69                                  array(
  70                                          'eventid' => new external_value(PARAM_INT, 'Event ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
  71                                          'repeat'  => new external_value(PARAM_BOOL, 'Delete comeplete series if repeated event')
  72                                  ), 'List of events to delete'
  73                          )
  74                      )
  75                  )
  76          );
  77      }
  78  
  79      /**
  80       * Delete Calendar events
  81       *
  82       * @param array $eventids A list of event ids with repeat flag to delete
  83       * @return null
  84       * @since Moodle 2.5
  85       */
  86      public static function delete_calendar_events($events) {
  87          global $DB;
  88  
  89          // Parameter validation.
  90          $params = self::validate_parameters(self:: delete_calendar_events_parameters(), array('events' => $events));
  91  
  92          $transaction = $DB->start_delegated_transaction();
  93  
  94          foreach ($params['events'] as $event) {
  95              $eventobj = calendar_event::load($event['eventid']);
  96  
  97              // Let's check if the user is allowed to delete an event.
  98              if (!calendar_delete_event_allowed($eventobj)) {
  99                  throw new moodle_exception('nopermissions', 'error', '', get_string('deleteevent', 'calendar'));
 100              }
 101              // Time to do the magic.
 102              $eventobj->delete($event['repeat']);
 103          }
 104  
 105          // Everything done smoothly, let's commit.
 106          $transaction->allow_commit();
 107  
 108          return null;
 109      }
 110  
 111      /**
 112       * Returns description of method result value
 113       *
 114       * @return \core_external\external_description
 115       * @since Moodle 2.5
 116       */
 117      public static function  delete_calendar_events_returns() {
 118          return null;
 119      }
 120  
 121      /**
 122       * Returns description of method parameters
 123       *
 124       * @return external_function_parameters
 125       * @since Moodle 2.5
 126       */
 127      public static function get_calendar_events_parameters() {
 128          return new external_function_parameters(
 129                  array('events' => new external_single_structure(
 130                              array(
 131                                      'eventids' => new external_multiple_structure(
 132                                              new external_value(PARAM_INT, 'event ids')
 133                                              , 'List of event ids',
 134                                              VALUE_DEFAULT, array()),
 135                                      'courseids' => new external_multiple_structure(
 136                                              new external_value(PARAM_INT, 'course ids')
 137                                              , 'List of course ids for which events will be returned',
 138                                              VALUE_DEFAULT, array()),
 139                                      'groupids' => new external_multiple_structure(
 140                                              new external_value(PARAM_INT, 'group ids')
 141                                              , 'List of group ids for which events should be returned',
 142                                              VALUE_DEFAULT, array()),
 143                                      'categoryids' => new external_multiple_structure(
 144                                              new external_value(PARAM_INT, 'Category ids'),
 145                                              'List of category ids for which events will be returned',
 146                                              VALUE_DEFAULT, array()),
 147                              ), 'Event details', VALUE_DEFAULT, array()),
 148                      'options' => new external_single_structure(
 149                              array(
 150                                      'userevents' => new external_value(PARAM_BOOL,
 151                                               "Set to true to return current user's user events",
 152                                               VALUE_DEFAULT, true, NULL_ALLOWED),
 153                                      'siteevents' => new external_value(PARAM_BOOL,
 154                                               "Set to true to return site events",
 155                                               VALUE_DEFAULT, true, NULL_ALLOWED),
 156                                      'timestart' => new external_value(PARAM_INT,
 157                                               "Time from which events should be returned",
 158                                               VALUE_DEFAULT, 0, NULL_ALLOWED),
 159                                      'timeend' => new external_value(PARAM_INT,
 160                                               "Time to which the events should be returned. We treat 0 and null as no end",
 161                                               VALUE_DEFAULT, 0, NULL_ALLOWED),
 162                                      'ignorehidden' => new external_value(PARAM_BOOL,
 163                                               "Ignore hidden events or not",
 164                                               VALUE_DEFAULT, true, NULL_ALLOWED),
 165  
 166                              ), 'Options', VALUE_DEFAULT, array())
 167                  )
 168          );
 169      }
 170  
 171      /**
 172       * Get Calendar events
 173       *
 174       * @param array $events A list of events
 175       * @param array $options various options
 176       * @return array Array of event details
 177       * @since Moodle 2.5
 178       */
 179      public static function get_calendar_events($events = array(), $options = array()) {
 180          global $SITE, $DB, $USER;
 181  
 182          // Parameter validation.
 183          $params = self::validate_parameters(self::get_calendar_events_parameters(), array('events' => $events, 'options' => $options));
 184          $funcparam = array('courses' => array(), 'groups' => array(), 'categories' => array());
 185          $hassystemcap = has_capability('moodle/calendar:manageentries', context_system::instance());
 186          $warnings = array();
 187          $coursecategories = array();
 188  
 189          // Let us find out courses and their categories that we can return events from.
 190          if (!$hassystemcap) {
 191              $courseobjs = enrol_get_my_courses();
 192              $courses = array_keys($courseobjs);
 193  
 194              $coursecategories = array_flip(array_map(function($course) {
 195                  return $course->category;
 196              }, $courseobjs));
 197  
 198              foreach ($params['events']['courseids'] as $id) {
 199                 try {
 200                      $context = context_course::instance($id);
 201                      self::validate_context($context);
 202                      $funcparam['courses'][] = $id;
 203                  } catch (Exception $e) {
 204                      $warnings[] = array(
 205                          'item' => 'course',
 206                          'itemid' => $id,
 207                          'warningcode' => 'nopermissions',
 208                          'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
 209                      );
 210                  }
 211              }
 212          } else {
 213              $courses = $params['events']['courseids'];
 214              $funcparam['courses'] = $courses;
 215  
 216              if (!empty($courses)) {
 217                  list($wheresql, $sqlparams) = $DB->get_in_or_equal($courses);
 218                  $wheresql = "id $wheresql";
 219                  $coursecategories = array_flip(array_map(function($course) {
 220                      return $course->category;
 221                  }, $DB->get_records_select('course', $wheresql, $sqlparams, '', 'category')));
 222              }
 223          }
 224  
 225          // Let us findout groups that we can return events from.
 226          if (!$hassystemcap) {
 227              $groups = groups_get_my_groups();
 228              $groups = array_keys($groups);
 229              foreach ($params['events']['groupids'] as $id) {
 230                  if (in_array($id, $groups)) {
 231                      $funcparam['groups'][] = $id;
 232                  } else {
 233                      $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this group');
 234                  }
 235              }
 236          } else {
 237              $groups = $params['events']['groupids'];
 238              $funcparam['groups'] = $groups;
 239          }
 240  
 241          $categories = array();
 242          if ($hassystemcap || !empty($courses)) {
 243              // Use the category id as the key in the following array. That way we do not have to remove duplicates and
 244              // have a faster lookup later.
 245              $categories = [];
 246  
 247              if (!empty($params['events']['categoryids'])) {
 248                  $catobjs = \core_course_category::get_many(
 249                      array_merge($params['events']['categoryids'], array_keys($coursecategories)));
 250                  foreach ($catobjs as $catobj) {
 251                      if (isset($coursecategories[$catobj->id]) ||
 252                              has_capability('moodle/category:manage', $catobj->get_context())) {
 253                          // If the user has access to a course in this category or can manage the category,
 254                          // then they can see all parent categories too.
 255                          $categories[$catobj->id] = true;
 256                          foreach ($catobj->get_parents() as $catid) {
 257                              $categories[$catid] = true;
 258                          }
 259                      }
 260                  }
 261                  $funcparam['categories'] = array_keys($categories);
 262              } else {
 263                  // Fetch all categories where this user has any enrolment, and all categories that this user can manage.
 264                  $calcatcache = cache::make('core', 'calendar_categories');
 265                  // Do not use cache if the user has the system capability as $coursecategories might not represent the
 266                  // courses the user is enrolled in.
 267                  $categories = (!$hassystemcap) ? $calcatcache->get('site') : false;
 268                  if ($categories !== false) {
 269                      // The ids are stored in a list in the cache.
 270                      $funcparam['categories'] = $categories;
 271                      $categories = array_flip($categories);
 272                  } else {
 273                      $categories = [];
 274                      foreach (\core_course_category::get_all() as $category) {
 275                          if (isset($coursecategories[$category->id]) ||
 276                                  has_capability('moodle/category:manage', $category->get_context(), $USER, false)) {
 277                              // If the user has access to a course in this category or can manage the category,
 278                              // then they can see all parent categories too.
 279                              $categories[$category->id] = true;
 280                              foreach ($category->get_parents() as $catid) {
 281                                  $categories[$catid] = true;
 282                              }
 283                          }
 284                      }
 285                      $funcparam['categories'] = array_keys($categories);
 286                      if (!$hassystemcap) {
 287                          $calcatcache->set('site', $funcparam['categories']);
 288                      }
 289                  }
 290              }
 291          }
 292  
 293          // Do we need user events?
 294          if (!empty($params['options']['userevents'])) {
 295              $funcparam['users'] = array($USER->id);
 296          } else {
 297              $funcparam['users'] = false;
 298          }
 299  
 300          // Do we need site events?
 301          if (!empty($params['options']['siteevents'])) {
 302              $funcparam['courses'][] = $SITE->id;
 303          }
 304  
 305          // We treat 0 and null as no end.
 306          if (empty($params['options']['timeend'])) {
 307              $params['options']['timeend'] = PHP_INT_MAX;
 308          }
 309  
 310          // Event list does not check visibility and permissions, we'll check that later.
 311          $eventlist = calendar_get_legacy_events($params['options']['timestart'], $params['options']['timeend'],
 312                  $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true,
 313                  $params['options']['ignorehidden'], $funcparam['categories']);
 314  
 315          // WS expects arrays.
 316          $events = array();
 317  
 318          // We need to get events asked for eventids.
 319          if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) {
 320              $eventlist += $eventsbyid;
 321          }
 322          foreach ($eventlist as $eventid => $eventobj) {
 323              $event = (array) $eventobj;
 324              // Description formatting.
 325              $calendareventobj = new calendar_event($event);
 326              $event['name'] = $calendareventobj->format_external_name();
 327              list($event['description'], $event['format']) = $calendareventobj->format_external_text();
 328  
 329              if ($hassystemcap) {
 330                  // User can see everything, no further check is needed.
 331                  $events[$eventid] = $event;
 332              } else if (!empty($eventobj->modulename)) {
 333                  $courseid = $eventobj->courseid;
 334                  if (!$courseid) {
 335                      if (!$calendareventobj->context || !($context = $calendareventobj->context->get_course_context(false))) {
 336                          continue;
 337                      }
 338                      $courseid = $context->instanceid;
 339                  }
 340                  $instances = get_fast_modinfo($courseid)->get_instances_of($eventobj->modulename);
 341                  if (!empty($instances[$eventobj->instance]->uservisible)) {
 342                      $events[$eventid] = $event;
 343                  }
 344              } else {
 345                  // Can the user actually see this event?
 346                  $eventobj = calendar_event::load($eventobj);
 347                  if ((($eventobj->courseid == $SITE->id) && (empty($eventobj->categoryid))) ||
 348                              (!empty($eventobj->categoryid) && isset($categories[$eventobj->categoryid])) ||
 349                              (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) ||
 350                              (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) ||
 351                              ($USER->id == $eventobj->userid) ||
 352                              (calendar_edit_event_allowed($eventobj))) {
 353                      $events[$eventid] = $event;
 354                  } else {
 355                      $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event');
 356                  }
 357              }
 358          }
 359          return array('events' => $events, 'warnings' => $warnings);
 360      }
 361  
 362      /**
 363       * Returns description of method result value
 364       *
 365       * @return \core_external\external_description
 366       * @since Moodle 2.5
 367       */
 368      public static function  get_calendar_events_returns() {
 369          return new external_single_structure(array(
 370                  'events' => new external_multiple_structure( new external_single_structure(
 371                          array(
 372                              'id' => new external_value(PARAM_INT, 'event id'),
 373                              'name' => new external_value(PARAM_RAW, 'event name'),
 374                              'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL, null, NULL_ALLOWED),
 375                              'format' => new external_format_value('description'),
 376                              'courseid' => new external_value(PARAM_INT, 'course id'),
 377                              'categoryid' => new external_value(PARAM_INT, 'Category id (only for category events).',
 378                                  VALUE_OPTIONAL),
 379                              'groupid' => new external_value(PARAM_INT, 'group id'),
 380                              'userid' => new external_value(PARAM_INT, 'user id'),
 381                              'repeatid' => new external_value(PARAM_INT, 'repeat id'),
 382                              'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL, null, NULL_ALLOWED),
 383                              'instance' => new external_value(PARAM_INT, 'instance id'),
 384                              'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
 385                              'timestart' => new external_value(PARAM_INT, 'timestart'),
 386                              'timeduration' => new external_value(PARAM_INT, 'time duration'),
 387                              'visible' => new external_value(PARAM_INT, 'visible'),
 388                              'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, null, NULL_NOT_ALLOWED),
 389                              'sequence' => new external_value(PARAM_INT, 'sequence'),
 390                              'timemodified' => new external_value(PARAM_INT, 'time modified'),
 391                              'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL, null, NULL_ALLOWED),
 392                          ), 'event')
 393                   ),
 394                   'warnings' => new external_warnings()
 395                  )
 396          );
 397      }
 398  
 399      /**
 400       * Returns description of method parameters.
 401       *
 402       * @since Moodle 3.3
 403       * @return external_function_parameters
 404       */
 405      public static function get_calendar_action_events_by_timesort_parameters() {
 406          return new external_function_parameters(
 407              array(
 408                  'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, 0),
 409                  'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
 410                  'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
 411                  'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20),
 412                  'limittononsuspendedevents' => new external_value(PARAM_BOOL,
 413                          'Limit the events to courses the user is not suspended in', VALUE_DEFAULT, false),
 414                  'userid' => new external_value(PARAM_INT, 'The user id', VALUE_DEFAULT, null),
 415                  'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null)
 416              )
 417          );
 418      }
 419  
 420      /**
 421       * Get calendar action events based on the timesort value.
 422       *
 423       * @since Moodle 3.3
 424       * @param null|int $timesortfrom Events after this time (inclusive)
 425       * @param null|int $timesortto Events before this time (inclusive)
 426       * @param null|int $aftereventid Get events with ids greater than this one
 427       * @param int $limitnum Limit the number of results to this value
 428       * @param null|int $userid The user id
 429       * @param string|null $searchvalue The value a user wishes to search against
 430       * @return array
 431       */
 432      public static function get_calendar_action_events_by_timesort($timesortfrom = 0, $timesortto = null,
 433                                                         $aftereventid = 0, $limitnum = 20, $limittononsuspendedevents = false,
 434                                                         $userid = null, ?string $searchvalue = null) {
 435          global $PAGE, $USER;
 436  
 437          $params = self::validate_parameters(
 438              self::get_calendar_action_events_by_timesort_parameters(),
 439              [
 440                  'timesortfrom' => $timesortfrom,
 441                  'timesortto' => $timesortto,
 442                  'aftereventid' => $aftereventid,
 443                  'limitnum' => $limitnum,
 444                  'limittononsuspendedevents' => $limittononsuspendedevents,
 445                  'userid' => $userid,
 446                  'searchvalue' => $searchvalue
 447              ]
 448          );
 449          if ($params['userid']) {
 450              $user = \core_user::get_user($params['userid']);
 451          } else {
 452              $user = $USER;
 453          }
 454  
 455          $context = \context_user::instance($user->id);
 456          self::validate_context($context);
 457  
 458          if ($params['userid'] && $USER->id !== $params['userid'] && !has_capability('moodle/calendar:manageentries', $context)) {
 459              throw new \required_capability_exception($context, 'moodle/calendar:manageentries', 'nopermission', '');
 460          }
 461  
 462          if (empty($params['aftereventid'])) {
 463              $params['aftereventid'] = null;
 464          }
 465  
 466          $renderer = $PAGE->get_renderer('core_calendar');
 467          $events = local_api::get_action_events_by_timesort(
 468              $params['timesortfrom'],
 469              $params['timesortto'],
 470              $params['aftereventid'],
 471              $params['limitnum'],
 472              $params['limittononsuspendedevents'],
 473              $user,
 474              clean_param($params['searchvalue'], PARAM_TEXT)
 475          );
 476  
 477          $exportercache = new events_related_objects_cache($events);
 478          $exporter = new events_exporter($events, ['cache' => $exportercache]);
 479  
 480          return $exporter->export($renderer);
 481      }
 482  
 483      /**
 484       * Returns description of method result value.
 485       *
 486       * @since Moodle 3.3
 487       * @return \core_external\external_description
 488       */
 489      public static function get_calendar_action_events_by_timesort_returns() {
 490          return events_exporter::get_read_structure();
 491      }
 492  
 493      /**
 494       * Returns description of method parameters.
 495       *
 496       * @return external_function_parameters
 497       */
 498      public static function get_calendar_action_events_by_course_parameters() {
 499          return new external_function_parameters(
 500              array(
 501                  'courseid' => new external_value(PARAM_INT, 'Course id'),
 502                  'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null),
 503                  'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
 504                  'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
 505                  'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20),
 506                  'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null)
 507              )
 508          );
 509      }
 510  
 511      /**
 512       * Get calendar action events for the given course.
 513       *
 514       * @since Moodle 3.3
 515       * @param int $courseid Only events in this course
 516       * @param null|int $timesortfrom Events after this time (inclusive)
 517       * @param null|int $timesortto Events before this time (inclusive)
 518       * @param null|int $aftereventid Get events with ids greater than this one
 519       * @param int $limitnum Limit the number of results to this value
 520       * @param string|null $searchvalue The value a user wishes to search against
 521       * @return array
 522       */
 523      public static function get_calendar_action_events_by_course(
 524          $courseid, $timesortfrom = null, $timesortto = null, $aftereventid = 0, $limitnum = 20, ?string $searchvalue = null) {
 525  
 526          global $PAGE, $USER;
 527  
 528          $user = null;
 529          $params = self::validate_parameters(
 530              self::get_calendar_action_events_by_course_parameters(),
 531              [
 532                  'courseid' => $courseid,
 533                  'timesortfrom' => $timesortfrom,
 534                  'timesortto' => $timesortto,
 535                  'aftereventid' => $aftereventid,
 536                  'limitnum' => $limitnum,
 537                  'searchvalue' => $searchvalue
 538              ]
 539          );
 540          $context = \context_user::instance($USER->id);
 541          self::validate_context($context);
 542  
 543          if (empty($params['aftereventid'])) {
 544              $params['aftereventid'] = null;
 545          }
 546  
 547          $courses = enrol_get_my_courses('*', null, 0, [$courseid]);
 548          $courses = array_values($courses);
 549  
 550          if (empty($courses)) {
 551              return [];
 552          }
 553  
 554          $course = $courses[0];
 555          $renderer = $PAGE->get_renderer('core_calendar');
 556          $events = local_api::get_action_events_by_course(
 557              $course,
 558              $params['timesortfrom'],
 559              $params['timesortto'],
 560              $params['aftereventid'],
 561              $params['limitnum'],
 562              clean_param($params['searchvalue'], PARAM_TEXT)
 563          );
 564  
 565          $exportercache = new events_related_objects_cache($events, $courses);
 566          $exporter = new events_exporter($events, ['cache' => $exportercache]);
 567  
 568          return $exporter->export($renderer);
 569      }
 570  
 571      /**
 572       * Returns description of method result value.
 573       *
 574       * @return \core_external\external_description
 575       */
 576      public static function get_calendar_action_events_by_course_returns() {
 577          return events_exporter::get_read_structure();
 578      }
 579  
 580      /**
 581       * Returns description of method parameters.
 582       *
 583       * @return external_function_parameters
 584       */
 585      public static function get_calendar_action_events_by_courses_parameters() {
 586          return new external_function_parameters(
 587              array(
 588                  'courseids' => new external_multiple_structure(
 589                      new external_value(PARAM_INT, 'Course id')
 590                  ),
 591                  'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null),
 592                  'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
 593                  'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 10),
 594                  'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null)
 595              )
 596          );
 597      }
 598  
 599      /**
 600       * Get calendar action events for a given list of courses.
 601       *
 602       * @since Moodle 3.3
 603       * @param array $courseids Only include events for these courses
 604       * @param null|int $timesortfrom Events after this time (inclusive)
 605       * @param null|int $timesortto Events before this time (inclusive)
 606       * @param int $limitnum Limit the number of results per course to this value
 607       * @param string|null $searchvalue The value a user wishes to search against
 608       * @return array
 609       */
 610      public static function get_calendar_action_events_by_courses(
 611          array $courseids, $timesortfrom = null, $timesortto = null, $limitnum = 10, ?string $searchvalue = null) {
 612  
 613          global $PAGE, $USER;
 614  
 615          $user = null;
 616          $params = self::validate_parameters(
 617              self::get_calendar_action_events_by_courses_parameters(),
 618              [
 619                  'courseids' => $courseids,
 620                  'timesortfrom' => $timesortfrom,
 621                  'timesortto' => $timesortto,
 622                  'limitnum' => $limitnum,
 623                  'searchvalue' => $searchvalue
 624              ]
 625          );
 626          $context = \context_user::instance($USER->id);
 627          self::validate_context($context);
 628  
 629          if (empty($params['courseids'])) {
 630              return ['groupedbycourse' => []];
 631          }
 632  
 633          $renderer = $PAGE->get_renderer('core_calendar');
 634          $courses = enrol_get_my_courses('*', null, 0, $params['courseids']);
 635          $courses = array_values($courses);
 636  
 637          if (empty($courses)) {
 638              return ['groupedbycourse' => []];
 639          }
 640  
 641          $events = local_api::get_action_events_by_courses(
 642              $courses,
 643              $params['timesortfrom'],
 644              $params['timesortto'],
 645              $params['limitnum'],
 646              clean_param($params['searchvalue'], PARAM_TEXT)
 647          );
 648  
 649          if (empty($events)) {
 650              return ['groupedbycourse' => []];
 651          }
 652  
 653          $exportercache = new events_related_objects_cache($events, $courses);
 654          $exporter = new events_grouped_by_course_exporter($events, ['cache' => $exportercache]);
 655  
 656          return $exporter->export($renderer);
 657      }
 658  
 659      /**
 660       * Returns description of method result value.
 661       *
 662       * @return \core_external\external_description
 663       */
 664      public static function get_calendar_action_events_by_courses_returns() {
 665          return events_grouped_by_course_exporter::get_read_structure();
 666      }
 667  
 668      /**
 669       * Returns description of method parameters.
 670       *
 671       * @return external_function_parameters.
 672       * @since Moodle 2.5
 673       */
 674      public static function create_calendar_events_parameters() {
 675          // Userid is always current user, so no need to get it from client.
 676          // Module based calendar events are not allowed here. Hence no need of instance and modulename.
 677          // subscription id and uuid is not allowed as this is not an ical api.
 678          return new external_function_parameters(
 679                  array('events' => new external_multiple_structure(
 680                          new external_single_structure(
 681                              array(
 682                                  'name' => new external_value(PARAM_TEXT, 'event name', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
 683                                  'description' => new external_value(PARAM_RAW, 'Description', VALUE_DEFAULT, null, NULL_ALLOWED),
 684                                  'format' => new external_format_value('description', VALUE_DEFAULT),
 685                                  'courseid' => new external_value(PARAM_INT, 'course id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
 686                                  'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
 687                                  'repeats' => new external_value(PARAM_INT, 'number of repeats', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
 688                                  'eventtype' => new external_value(PARAM_TEXT, 'Event type', VALUE_DEFAULT, 'user', NULL_NOT_ALLOWED),
 689                                  'timestart' => new external_value(PARAM_INT, 'timestart', VALUE_DEFAULT, time(), NULL_NOT_ALLOWED),
 690                                  'timeduration' => new external_value(PARAM_INT, 'time duration', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
 691                                  'visible' => new external_value(PARAM_INT, 'visible', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
 692                                  'sequence' => new external_value(PARAM_INT, 'sequence', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
 693                              ), 'event')
 694                  )
 695              )
 696          );
 697      }
 698  
 699      /**
 700       * Create calendar events.
 701       *
 702       * @param array $events A list of events to create.
 703       * @return array array of events created.
 704       * @since Moodle 2.5
 705       * @throws moodle_exception if user doesnt have the permission to create events.
 706       */
 707      public static function create_calendar_events($events) {
 708          global $DB, $USER;
 709  
 710          // Parameter validation.
 711          $params = self::validate_parameters(self::create_calendar_events_parameters(), array('events' => $events));
 712  
 713          $transaction = $DB->start_delegated_transaction();
 714          $return = array();
 715          $warnings = array();
 716  
 717          foreach ($params['events'] as $event) {
 718  
 719              // Let us set some defaults.
 720              $event['userid'] = $USER->id;
 721              $event['modulename'] = '';
 722              $event['instance'] = 0;
 723              $event['subscriptionid'] = null;
 724              $event['uuid']= '';
 725              $event['format'] = external_util::validate_format($event['format']);
 726              if ($event['repeats'] > 0) {
 727                  $event['repeat'] = 1;
 728              } else {
 729                  $event['repeat'] = 0;
 730              }
 731  
 732              $eventobj = new calendar_event($event);
 733  
 734              // Let's check if the user is allowed to delete an event.
 735              if (!calendar_add_event_allowed($eventobj)) {
 736                  $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event');
 737                  continue;
 738              }
 739              // Let's create the event.
 740              $var = $eventobj->create($event);
 741              $var = (array)$var->properties();
 742              if ($event['repeat']) {
 743                  $children = $DB->get_records('event', array('repeatid' => $var['id']));
 744                  foreach ($children as $child) {
 745                      $return[] = (array) $child;
 746                  }
 747              } else {
 748                  $return[] = $var;
 749              }
 750          }
 751  
 752          // Everything done smoothly, let's commit.
 753          $transaction->allow_commit();
 754          return array('events' => $return, 'warnings' => $warnings);
 755      }
 756  
 757      /**
 758       * Returns description of method result value.
 759       *
 760       * @return \core_external\external_description.
 761       * @since Moodle 2.5
 762       */
 763      public static function  create_calendar_events_returns() {
 764              return new external_single_structure(
 765                      array(
 766                          'events' => new external_multiple_structure( new external_single_structure(
 767                                  array(
 768                                      'id' => new external_value(PARAM_INT, 'event id'),
 769                                      'name' => new external_value(PARAM_RAW, 'event name'),
 770                                      'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL),
 771                                      'format' => new external_format_value('description'),
 772                                      'courseid' => new external_value(PARAM_INT, 'course id'),
 773                                      'groupid' => new external_value(PARAM_INT, 'group id'),
 774                                      'userid' => new external_value(PARAM_INT, 'user id'),
 775                                      'repeatid' => new external_value(PARAM_INT, 'repeat id', VALUE_OPTIONAL),
 776                                      'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL),
 777                                      'instance' => new external_value(PARAM_INT, 'instance id'),
 778                                      'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
 779                                      'timestart' => new external_value(PARAM_INT, 'timestart'),
 780                                      'timeduration' => new external_value(PARAM_INT, 'time duration'),
 781                                      'visible' => new external_value(PARAM_INT, 'visible'),
 782                                      'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 783                                      'sequence' => new external_value(PARAM_INT, 'sequence'),
 784                                      'timemodified' => new external_value(PARAM_INT, 'time modified'),
 785                                      'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL),
 786                                  ), 'event')
 787                          ),
 788                        'warnings' => new external_warnings()
 789                      )
 790              );
 791      }
 792  
 793      /**
 794       * Returns description of method parameters.
 795       *
 796       * @return external_function_parameters
 797       */
 798      public static function get_calendar_event_by_id_parameters() {
 799          return new external_function_parameters(
 800              array(
 801                  'eventid' => new external_value(PARAM_INT, 'The event id to be retrieved'),
 802              )
 803          );
 804      }
 805  
 806      /**
 807       * Get calendar event by id.
 808       *
 809       * @param int $eventid The calendar event id to be retrieved.
 810       * @return array Array of event details
 811       */
 812      public static function get_calendar_event_by_id($eventid) {
 813          global $PAGE, $USER;
 814  
 815          $params = self::validate_parameters(self::get_calendar_event_by_id_parameters(), ['eventid' => $eventid]);
 816          $context = \context_user::instance($USER->id);
 817  
 818          self::validate_context($context);
 819          $warnings = array();
 820  
 821          $eventvault = event_container::get_event_vault();
 822          if ($event = $eventvault->get_event_by_id($params['eventid'])) {
 823              $mapper = event_container::get_event_mapper();
 824              if (!calendar_view_event_allowed($mapper->from_event_to_legacy_event($event))) {
 825                  throw new moodle_exception('nopermissiontoviewcalendar', 'error');
 826              }
 827          }
 828  
 829          if (!$event) {
 830              // We can't return a warning in this case because the event is not optional.
 831              // We don't know the context for the event and it's not worth loading it.
 832              $syscontext = context_system::instance();
 833              throw new \required_capability_exception($syscontext, 'moodle/course:view', 'nopermissions', 'error');
 834          }
 835  
 836          $cache = new events_related_objects_cache([$event]);
 837          $relatedobjects = [
 838              'context' => $cache->get_context($event),
 839              'course' => $cache->get_course($event),
 840          ];
 841  
 842          $exporter = new event_exporter($event, $relatedobjects);
 843          $renderer = $PAGE->get_renderer('core_calendar');
 844  
 845          return array('event' => $exporter->export($renderer), 'warnings' => $warnings);
 846      }
 847  
 848      /**
 849       * Returns description of method result value
 850       *
 851       * @return \core_external\external_description
 852       */
 853      public static function get_calendar_event_by_id_returns() {
 854          $eventstructure = event_exporter::get_read_structure();
 855  
 856          return new external_single_structure(array(
 857              'event' => $eventstructure,
 858              'warnings' => new external_warnings()
 859              )
 860          );
 861      }
 862  
 863      /**
 864       * Returns description of method parameters.
 865       *
 866       * @return external_function_parameters.
 867       */
 868      public static function submit_create_update_form_parameters() {
 869          return new external_function_parameters(
 870              [
 871                  'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
 872              ]
 873          );
 874      }
 875  
 876      /**
 877       * Handles the event form submission.
 878       *
 879       * @param string $formdata The event form data in a URI encoded param string
 880       * @return array The created or modified event
 881       * @throws moodle_exception
 882       */
 883      public static function submit_create_update_form($formdata) {
 884          global $USER, $PAGE, $CFG;
 885          require_once($CFG->libdir."/filelib.php");
 886  
 887          // Parameter validation.
 888          $params = self::validate_parameters(self::submit_create_update_form_parameters(), ['formdata' => $formdata]);
 889          $context = \context_user::instance($USER->id);
 890          $data = [];
 891  
 892          self::validate_context($context);
 893          parse_str($params['formdata'], $data);
 894  
 895          if (WS_SERVER) {
 896              // Request via WS, ignore sesskey checks in form library.
 897              $USER->ignoresesskey = true;
 898          }
 899  
 900          $eventtype = isset($data['eventtype']) ? $data['eventtype'] : null;
 901          $coursekey = ($eventtype == 'group') ? 'groupcourseid' : 'courseid';
 902          $courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null;
 903          $editoroptions = \core_calendar\local\event\forms\create::build_editor_options($context);
 904          $formoptions = ['editoroptions' => $editoroptions, 'courseid' => $courseid];
 905          $formoptions['eventtypes'] = calendar_get_allowed_event_types($courseid);
 906          if ($courseid) {
 907              require_once($CFG->libdir . '/grouplib.php');
 908              $groupcoursedata = groups_get_course_data($courseid);
 909              if (!empty($groupcoursedata->groups)) {
 910                  $formoptions['groups'] = [];
 911                  foreach ($groupcoursedata->groups as $groupid => $groupdata) {
 912                      $formoptions['groups'][$groupid] = $groupdata->name;
 913                  }
 914              }
 915          }
 916  
 917          if (!empty($data['id'])) {
 918              $eventid = clean_param($data['id'], PARAM_INT);
 919              $legacyevent = calendar_event::load($eventid);
 920              $legacyevent->count_repeats();
 921              $formoptions['event'] = $legacyevent;
 922              $mform = new update_event_form(null, $formoptions, 'post', '', null, true, $data);
 923          } else {
 924              $legacyevent = null;
 925              $mform = new create_event_form(null, $formoptions, 'post', '', null, true, $data);
 926          }
 927  
 928          if ($validateddata = $mform->get_data()) {
 929              $formmapper = new create_update_form_mapper();
 930              $properties = $formmapper->from_data_to_event_properties($validateddata);
 931  
 932              if (is_null($legacyevent)) {
 933                  $legacyevent = new \calendar_event($properties);
 934                  // Need to do this in order to initialise the description
 935                  // property which then triggers the update function below
 936                  // to set the appropriate default properties on the event.
 937                  $properties = $legacyevent->properties(true);
 938              }
 939  
 940              if (!calendar_edit_event_allowed($legacyevent, true)) {
 941                  throw new \moodle_exception('nopermissiontoupdatecalendar');
 942              }
 943  
 944              $legacyevent->update($properties);
 945              $eventcontext = $legacyevent->context;
 946  
 947              file_remove_editor_orphaned_files($validateddata->description);
 948  
 949              // Take any files added to the description draft file area and
 950              // convert them into the proper event description file area. Also
 951              // parse the description text and replace the URLs to the draft files
 952              // with the @@PLUGIN_FILE@@ placeholder to be persisted in the DB.
 953              $description = file_save_draft_area_files(
 954                  $validateddata->description['itemid'],
 955                  $eventcontext->id,
 956                  'calendar',
 957                  'event_description',
 958                  $legacyevent->id,
 959                  create_event_form::build_editor_options($eventcontext),
 960                  $validateddata->description['text']
 961              );
 962  
 963              // If draft files were found then we need to save the new
 964              // description value.
 965              if ($description != $validateddata->description['text']) {
 966                  $properties->id = $legacyevent->id;
 967                  $properties->description = $description;
 968                  $legacyevent->update($properties);
 969              }
 970  
 971              $eventmapper = event_container::get_event_mapper();
 972              $event = $eventmapper->from_legacy_event_to_event($legacyevent);
 973              $cache = new events_related_objects_cache([$event]);
 974              $relatedobjects = [
 975                  'context' => $cache->get_context($event),
 976                  'course' => $cache->get_course($event),
 977              ];
 978              $exporter = new event_exporter($event, $relatedobjects);
 979              $renderer = $PAGE->get_renderer('core_calendar');
 980  
 981              return [ 'event' => $exporter->export($renderer) ];
 982          } else {
 983              return [ 'validationerror' => true ];
 984          }
 985      }
 986  
 987      /**
 988       * Returns description of method result value.
 989       *
 990       * @return \core_external\external_description.
 991       */
 992      public static function  submit_create_update_form_returns() {
 993          $eventstructure = event_exporter::get_read_structure();
 994          $eventstructure->required = VALUE_OPTIONAL;
 995  
 996          return new external_single_structure(
 997              array(
 998                  'event' => $eventstructure,
 999                  'validationerror' => new external_value(PARAM_BOOL, 'Invalid form data', VALUE_DEFAULT, false),
1000              )
1001          );
1002      }
1003  
1004      /**
1005       * Get data for the monthly calendar view.
1006       *
1007       * @param int $year The year to be shown
1008       * @param int $month The month to be shown
1009       * @param int $courseid The course to be included
1010       * @param int $categoryid The category to be included
1011       * @param bool $includenavigation Whether to include navigation
1012       * @param bool $mini Whether to return the mini month view or not
1013       * @param int $day The day we want to keep as the current day
1014       * @param string|null $view The view mode for the calendar.
1015       * @return  array
1016       */
1017      public static function get_calendar_monthly_view($year, $month, $courseid, $categoryid, $includenavigation, $mini, $day,
1018              ?string $view = null) {
1019          global $USER, $PAGE;
1020  
1021          // Parameter validation.
1022          $params = self::validate_parameters(self::get_calendar_monthly_view_parameters(), [
1023              'year' => $year,
1024              'month' => $month,
1025              'courseid' => $courseid,
1026              'categoryid' => $categoryid,
1027              'includenavigation' => $includenavigation,
1028              'mini' => $mini,
1029              'day' => $day,
1030              'view' => $view,
1031          ]);
1032  
1033          $context = \context_user::instance($USER->id);
1034          self::validate_context($context);
1035          $PAGE->set_url('/calendar/');
1036  
1037          $type = \core_calendar\type_factory::get_calendar_instance();
1038  
1039          $time = $type->convert_to_timestamp($params['year'], $params['month'], $params['day']);
1040          $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']);
1041          self::validate_context($calendar->context);
1042  
1043          $view = $params['view'] ?? ($params['mini'] ? 'mini' : 'month');
1044          list($data, $template) = calendar_get_view($calendar, $view, $params['includenavigation']);
1045  
1046          return $data;
1047      }
1048  
1049      /**
1050       * Returns description of method parameters.
1051       *
1052       * @return external_function_parameters
1053       */
1054      public static function get_calendar_monthly_view_parameters() {
1055          return new external_function_parameters(
1056              [
1057                  'year' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED),
1058                  'month' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED),
1059                  'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
1060                  'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
1061                  'includenavigation' => new external_value(
1062                      PARAM_BOOL,
1063                      'Whether to show course navigation',
1064                      VALUE_DEFAULT,
1065                      true,
1066                      NULL_ALLOWED
1067                  ),
1068                  'mini' => new external_value(
1069                      PARAM_BOOL,
1070                      'Whether to return the mini month view or not',
1071                      VALUE_DEFAULT,
1072                      false,
1073                      NULL_ALLOWED
1074                  ),
1075                  'day' => new external_value(PARAM_INT, 'Day to be viewed', VALUE_DEFAULT, 1),
1076                  'view' => new external_value(PARAM_ALPHA, 'The view mode of the calendar', VALUE_DEFAULT, 'month', NULL_ALLOWED),
1077              ]
1078          );
1079      }
1080  
1081      /**
1082       * Returns description of method result value.
1083       *
1084       * @return \core_external\external_description
1085       */
1086      public static function get_calendar_monthly_view_returns() {
1087          return \core_calendar\external\month_exporter::get_read_structure();
1088      }
1089  
1090      /**
1091       * Get data for the daily calendar view.
1092       *
1093       * @param   int     $year The year to be shown
1094       * @param   int     $month The month to be shown
1095       * @param   int     $day The day to be shown
1096       * @param   int     $courseid The course to be included
1097       * @return  array
1098       */
1099      public static function get_calendar_day_view($year, $month, $day, $courseid, $categoryid) {
1100          global $DB, $USER, $PAGE;
1101  
1102          // Parameter validation.
1103          $params = self::validate_parameters(self::get_calendar_day_view_parameters(), [
1104              'year' => $year,
1105              'month' => $month,
1106              'day' => $day,
1107              'courseid' => $courseid,
1108              'categoryid' => $categoryid,
1109          ]);
1110  
1111          $context = \context_user::instance($USER->id);
1112          self::validate_context($context);
1113  
1114          $type = \core_calendar\type_factory::get_calendar_instance();
1115  
1116          $time = $type->convert_to_timestamp($params['year'], $params['month'], $params['day']);
1117          $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']);
1118          self::validate_context($calendar->context);
1119  
1120          list($data, $template) = calendar_get_view($calendar, 'day');
1121  
1122          return $data;
1123      }
1124  
1125      /**
1126       * Returns description of method parameters.
1127       *
1128       * @return external_function_parameters
1129       */
1130      public static function get_calendar_day_view_parameters() {
1131          return new external_function_parameters(
1132              [
1133                  'year' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED),
1134                  'month' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED),
1135                  'day' => new external_value(PARAM_INT, 'Day to be viewed', VALUE_REQUIRED),
1136                  'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
1137                  'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
1138              ]
1139          );
1140      }
1141  
1142      /**
1143       * Returns description of method result value.
1144       *
1145       * @return \core_external\external_description
1146       */
1147      public static function get_calendar_day_view_returns() {
1148          return \core_calendar\external\calendar_day_exporter::get_read_structure();
1149      }
1150  
1151  
1152      /**
1153       * Returns description of method parameters.
1154       *
1155       * @return external_function_parameters
1156       */
1157      public static function update_event_start_day_parameters() {
1158          return new external_function_parameters(
1159              [
1160                  'eventid' => new external_value(PARAM_INT, 'Id of event to be updated', VALUE_REQUIRED),
1161                  'daytimestamp' => new external_value(PARAM_INT, 'Timestamp for the new start day', VALUE_REQUIRED),
1162              ]
1163          );
1164      }
1165  
1166      /**
1167       * Change the start day for the given calendar event to the day that
1168       * corresponds with the provided timestamp.
1169       *
1170       * The timestamp only needs to be anytime within the desired day as only
1171       * the date data is extracted from it.
1172       *
1173       * The event's original time of day is maintained, only the date is shifted.
1174       *
1175       * @param int $eventid Id of event to be updated
1176       * @param int $daytimestamp Timestamp for the new start day
1177       * @return  array
1178       */
1179      public static function update_event_start_day($eventid, $daytimestamp) {
1180          global $USER, $PAGE;
1181  
1182          // Parameter validation.
1183          $params = self::validate_parameters(self::update_event_start_day_parameters(), [
1184              'eventid' => $eventid,
1185              'daytimestamp' => $daytimestamp,
1186          ]);
1187  
1188          $vault = event_container::get_event_vault();
1189          $mapper = event_container::get_event_mapper();
1190          $event = $vault->get_event_by_id($eventid);
1191  
1192          if (!$event) {
1193              throw new \moodle_exception('Unable to find event with id ' . $eventid);
1194          }
1195  
1196          $legacyevent = $mapper->from_event_to_legacy_event($event);
1197  
1198          if (!calendar_edit_event_allowed($legacyevent, true)) {
1199              throw new \moodle_exception('nopermissiontoupdatecalendar');
1200          }
1201  
1202          self::validate_context($legacyevent->context);
1203  
1204          $newdate = usergetdate($daytimestamp);
1205          $startdatestring = implode('-', [$newdate['year'], $newdate['mon'], $newdate['mday']]);
1206          $startdate = new DateTimeImmutable($startdatestring);
1207          $event = local_api::update_event_start_day($event, $startdate);
1208          $cache = new events_related_objects_cache([$event]);
1209          $relatedobjects = [
1210              'context' => $cache->get_context($event),
1211              'course' => $cache->get_course($event),
1212          ];
1213          $exporter = new event_exporter($event, $relatedobjects);
1214          $renderer = $PAGE->get_renderer('core_calendar');
1215  
1216          return array('event' => $exporter->export($renderer));
1217      }
1218  
1219      /**
1220       * Returns description of method result value.
1221       *
1222       * @return \core_external\external_description
1223       */
1224      public static function update_event_start_day_returns() {
1225          return new external_single_structure(
1226              array(
1227                  'event' => event_exporter::get_read_structure()
1228              )
1229          );
1230      }
1231  
1232      /**
1233       * Get data for the monthly calendar view.
1234       *
1235       * @param   int     $courseid The course to be included
1236       * @param   int     $categoryid The category to be included
1237       * @return  array
1238       */
1239      public static function get_calendar_upcoming_view($courseid, $categoryid) {
1240          global $DB, $USER, $PAGE;
1241  
1242          // Parameter validation.
1243          $params = self::validate_parameters(self::get_calendar_upcoming_view_parameters(), [
1244              'courseid' => $courseid,
1245              'categoryid' => $categoryid,
1246          ]);
1247  
1248          $context = \context_user::instance($USER->id);
1249          self::validate_context($context);
1250          $PAGE->set_url('/calendar/');
1251  
1252          $calendar = \calendar_information::create(time(), $params['courseid'], $params['categoryid']);
1253          self::validate_context($calendar->context);
1254  
1255          list($data, $template) = calendar_get_view($calendar, 'upcoming');
1256  
1257          return $data;
1258      }
1259  
1260      /**
1261       * Returns description of method parameters.
1262       *
1263       * @return external_function_parameters
1264       */
1265      public static function get_calendar_upcoming_view_parameters() {
1266          return new external_function_parameters(
1267              [
1268                  'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
1269                  'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
1270              ]
1271          );
1272      }
1273  
1274      /**
1275       * Returns description of method result value.
1276       *
1277       * @return \core_external\external_description
1278       */
1279      public static function get_calendar_upcoming_view_returns() {
1280          return \core_calendar\external\calendar_upcoming_exporter::get_read_structure();
1281      }
1282  
1283  
1284      /**
1285       * Returns description of method parameters.
1286       *
1287       * @return external_function_parameters.
1288       * @since  Moodle 3.7
1289       */
1290      public static function get_calendar_access_information_parameters() {
1291          return new external_function_parameters(
1292              [
1293                  'courseid' => new external_value(PARAM_INT, 'Course to check, empty for site calendar events.', VALUE_DEFAULT, 0),
1294              ]
1295          );
1296      }
1297  
1298      /**
1299       * Convenience function to retrieve some permissions information for the given course calendar.
1300       *
1301       * @param int $courseid Course to check, empty for site.
1302       * @return array The access information
1303       * @throws moodle_exception
1304       * @since  Moodle 3.7
1305       */
1306      public static function get_calendar_access_information($courseid = 0) {
1307  
1308          $params = self::validate_parameters(self::get_calendar_access_information_parameters(), ['courseid' => $courseid]);
1309  
1310          if (empty($params['courseid']) || $params['courseid'] == SITEID) {
1311              $context = \context_system::instance();
1312          } else {
1313              $context = \context_course::instance($params['courseid']);
1314          }
1315  
1316          self::validate_context($context);
1317  
1318          return [
1319              'canmanageentries' => has_capability('moodle/calendar:manageentries', $context),
1320              'canmanageownentries' => has_capability('moodle/calendar:manageownentries', $context),
1321              'canmanagegroupentries' => has_capability('moodle/calendar:managegroupentries', $context),
1322              'warnings' => [],
1323          ];
1324      }
1325  
1326      /**
1327       * Returns description of method result value.
1328       *
1329       * @return \core_external\external_description.
1330       * @since  Moodle 3.7
1331       */
1332      public static function  get_calendar_access_information_returns() {
1333  
1334          return new external_single_structure(
1335              [
1336                  'canmanageentries' => new external_value(PARAM_BOOL, 'Whether the user can manage entries.'),
1337                  'canmanageownentries' => new external_value(PARAM_BOOL, 'Whether the user can manage its own entries.'),
1338                  'canmanagegroupentries' => new external_value(PARAM_BOOL, 'Whether the user can manage group entries.'),
1339                  'warnings' => new external_warnings(),
1340              ]
1341          );
1342      }
1343  
1344      /**
1345       * Returns description of method parameters.
1346       *
1347       * @return external_function_parameters.
1348       * @since  Moodle 3.7
1349       */
1350      public static function get_allowed_event_types_parameters() {
1351          return new external_function_parameters(
1352              [
1353                  'courseid' => new external_value(PARAM_INT, 'Course to check, empty for site.', VALUE_DEFAULT, 0),
1354              ]
1355          );
1356      }
1357  
1358      /**
1359       * Get the type of events a user can create in the given course.
1360       *
1361       * @param int $courseid Course to check, empty for site.
1362       * @return array The types allowed
1363       * @throws moodle_exception
1364       * @since  Moodle 3.7
1365       */
1366      public static function get_allowed_event_types($courseid = 0) {
1367  
1368          $params = self::validate_parameters(self::get_allowed_event_types_parameters(), ['courseid' => $courseid]);
1369  
1370          if (empty($params['courseid']) || $params['courseid'] == SITEID) {
1371              $context = \context_system::instance();
1372          } else {
1373              $context = \context_course::instance($params['courseid']);
1374          }
1375  
1376          self::validate_context($context);
1377  
1378          $allowedeventtypes = array_filter(calendar_get_allowed_event_types($params['courseid']));
1379  
1380          return [
1381              'allowedeventtypes' => array_keys($allowedeventtypes),
1382              'warnings' => [],
1383          ];
1384      }
1385  
1386      /**
1387       * Returns description of method result value.
1388       *
1389       * @return \core_external\external_description.
1390       * @since  Moodle 3.7
1391       */
1392      public static function  get_allowed_event_types_returns() {
1393  
1394          return new external_single_structure(
1395              [
1396                  'allowedeventtypes' => new external_multiple_structure(
1397                      new external_value(PARAM_NOTAGS, 'Allowed event types to be created in the given course.')
1398                  ),
1399                  'warnings' => new external_warnings(),
1400              ]
1401          );
1402      }
1403  
1404      /**
1405       * Convert the specified dates into unix timestamps.
1406       *
1407       * @param   array $datetimes Array of arrays containing date time details, each in the format:
1408       *           ['year' => a, 'month' => b, 'day' => c,
1409       *            'hour' => d (optional), 'minute' => e (optional), 'key' => 'x' (optional)]
1410       * @return  array Provided array of dates converted to unix timestamps
1411       * @throws moodle_exception If one or more of the dates provided does not convert to a valid timestamp.
1412       */
1413      public static function get_timestamps($datetimes) {
1414          $params = self::validate_parameters(self::get_timestamps_parameters(), ['data' => $datetimes]);
1415  
1416          $type = \core_calendar\type_factory::get_calendar_instance();
1417          $timestamps = ['timestamps' => []];
1418  
1419          foreach ($params['data'] as $key => $datetime) {
1420              $hour = $datetime['hour'] ?? 0;
1421              $minute = $datetime['minute'] ?? 0;
1422  
1423              try {
1424                  $timestamp = $type->convert_to_timestamp(
1425                      $datetime['year'], $datetime['month'], $datetime['day'], $hour, $minute);
1426  
1427                  $timestamps['timestamps'][] = [
1428                      'key' => $datetime['key'] ?? $key,
1429                      'timestamp' => $timestamp,
1430                  ];
1431  
1432              } catch (Exception $e) {
1433                  throw new moodle_exception('One or more of the dates provided were invalid');
1434              }
1435          }
1436  
1437          return $timestamps;
1438      }
1439  
1440      /**
1441       * Describes the parameters for get_timestamps.
1442       *
1443       * @return external_function_parameters
1444       */
1445      public static function get_timestamps_parameters() {
1446          return new external_function_parameters ([
1447              'data' => new external_multiple_structure(
1448                  new external_single_structure(
1449                      [
1450                          'key' => new external_value(PARAM_ALPHANUMEXT, 'key', VALUE_OPTIONAL),
1451                          'year' => new external_value(PARAM_INT, 'year'),
1452                          'month' => new external_value(PARAM_INT, 'month'),
1453                          'day' => new external_value(PARAM_INT, 'day'),
1454                          'hour' => new external_value(PARAM_INT, 'hour', VALUE_OPTIONAL),
1455                          'minute' => new external_value(PARAM_INT, 'minute', VALUE_OPTIONAL),
1456                      ]
1457                  )
1458              )
1459          ]);
1460      }
1461  
1462      /**
1463       * Describes the timestamps return format.
1464       *
1465       * @return external_single_structure
1466       */
1467      public static function get_timestamps_returns() {
1468          return new external_single_structure(
1469              [
1470                  'timestamps' => new external_multiple_structure(
1471                      new external_single_structure(
1472                          [
1473                              'key' => new external_value(PARAM_ALPHANUMEXT, 'Timestamp key'),
1474                              'timestamp' => new external_value(PARAM_INT, 'Unix timestamp'),
1475                          ]
1476                      )
1477                  )
1478              ]
1479          );
1480      }
1481  }