Search moodle.org's
Developer Documentation

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

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