Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Contains class containing the internal calendar API.
  19   *
  20   * @package    core_calendar
  21   * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_calendar\local;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  use core_calendar\local\event\container;
  30  use core_calendar\local\event\entities\event_interface;
  31  use core_calendar\local\event\exceptions\limit_invalid_parameter_exception;
  32  
  33  /**
  34   * Class containing the local calendar API.
  35   *
  36   * This should not be used outside of core_calendar.
  37   *
  38   * @package    core_calendar
  39   * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class api {
  43      /**
  44       * Get all events restricted by various parameters, taking in to account user and group overrides.
  45       *
  46       * @param int|null      $timestartfrom         Events with timestart from this value (inclusive).
  47       * @param int|null      $timestartto           Events with timestart until this value (inclusive).
  48       * @param int|null      $timesortfrom          Events with timesort from this value (inclusive).
  49       * @param int|null      $timesortto            Events with timesort until this value (inclusive).
  50       * @param int|null      $timestartaftereventid Restrict the events in the timestart range to ones after this ID.
  51       * @param int|null      $timesortaftereventid  Restrict the events in the timesort range to ones after this ID.
  52       * @param int           $limitnum              Return at most this number of events.
  53       * @param int|null      $type                  Return only events of this type.
  54       * @param array|null    $usersfilter           Return only events for these users.
  55       * @param array|null    $groupsfilter          Return only events for these groups.
  56       * @param array|null    $coursesfilter         Return only events for these courses.
  57       * @param bool          $withduration          If true return only events starting within specified
  58       *                                             timestart otherwise return in progress events as well.
  59       * @param bool          $ignorehidden          If true don't return hidden events.
  60       * @return \core_calendar\local\event\entities\event_interface[] Array of event_interfaces.
  61       */
  62      public static function get_events(
  63          $timestartfrom = null,
  64          $timestartto = null,
  65          $timesortfrom = null,
  66          $timesortto = null,
  67          $timestartaftereventid = null,
  68          $timesortaftereventid = null,
  69          $limitnum = 20,
  70          $type = null,
  71          array $usersfilter = null,
  72          array $groupsfilter = null,
  73          array $coursesfilter = null,
  74          array $categoriesfilter = null,
  75          $withduration = true,
  76          $ignorehidden = true,
  77          callable $filter = null
  78      ) {
  79          global $USER;
  80  
  81          $vault = \core_calendar\local\event\container::get_event_vault();
  82  
  83          $timestartafterevent = null;
  84          $timesortafterevent = null;
  85  
  86          if ($timestartaftereventid && $event = $vault->get_event_by_id($timestartaftereventid)) {
  87              $timestartafterevent = $event;
  88          }
  89  
  90          if ($timesortaftereventid && $event = $vault->get_event_by_id($timesortaftereventid)) {
  91              $timesortafterevent = $event;
  92          }
  93  
  94          return $vault->get_events(
  95              $timestartfrom,
  96              $timestartto,
  97              $timesortfrom,
  98              $timesortto,
  99              $timestartafterevent,
 100              $timesortafterevent,
 101              $limitnum,
 102              $type,
 103              $usersfilter,
 104              $groupsfilter,
 105              $coursesfilter,
 106              $categoriesfilter,
 107              $withduration,
 108              $ignorehidden,
 109              $filter
 110          );
 111      }
 112  
 113      /**
 114       * Get a list of action events for the logged in user by the given
 115       * timesort values.
 116       *
 117       * @param int|null $timesortfrom The start timesort value (inclusive)
 118       * @param int|null $timesortto The end timesort value (inclusive)
 119       * @param int|null $aftereventid Only return events after this one
 120       * @param int $limitnum Limit results to this amount (between 1 and 50)
 121       * @param bool $lmittononsuspendedevents Limit course events to courses the user is active in (not suspended).
 122       * @param \stdClass|null $user The user id or false for $USER
 123       * @return array A list of action_event_interface objects
 124       * @throws \moodle_exception
 125       */
 126      public static function get_action_events_by_timesort(
 127          $timesortfrom = null,
 128          $timesortto = null,
 129          $aftereventid = null,
 130          $limitnum = 20,
 131          $limittononsuspendedevents = false,
 132          ?\stdClass $user = null
 133      ) {
 134          global $USER;
 135  
 136          if (!$user) {
 137              $user = $USER;
 138          }
 139  
 140          if (is_null($timesortfrom) && is_null($timesortto)) {
 141              throw new \moodle_exception("Must provide a timesort to and/or from value");
 142          }
 143  
 144          if ($limitnum < 1 || $limitnum > 50) {
 145              throw new \moodle_exception("Limit must be between 1 and 50 (inclusive)");
 146          }
 147  
 148          \core_calendar\local\event\container::set_requesting_user($user->id);
 149          $vault = \core_calendar\local\event\container::get_event_vault();
 150  
 151          $afterevent = null;
 152          if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
 153              $afterevent = $event;
 154          }
 155  
 156          return $vault->get_action_events_by_timesort($user, $timesortfrom, $timesortto, $afterevent, $limitnum,
 157                  $limittononsuspendedevents);
 158      }
 159  
 160      /**
 161       * Get a list of action events for the logged in user by the given
 162       * course and timesort values.
 163       *
 164       * @param \stdClass $course The course the events must belong to
 165       * @param int|null $timesortfrom The start timesort value (inclusive)
 166       * @param int|null $timesortto The end timesort value (inclusive)
 167       * @param int|null $aftereventid Only return events after this one
 168       * @param int $limitnum Limit results to this amount (between 1 and 50)
 169       * @return array A list of action_event_interface objects
 170       * @throws limit_invalid_parameter_exception
 171       */
 172      public static function get_action_events_by_course(
 173          $course,
 174          $timesortfrom = null,
 175          $timesortto = null,
 176          $aftereventid = null,
 177          $limitnum = 20
 178      ) {
 179          global $USER;
 180  
 181          if ($limitnum < 1 || $limitnum > 50) {
 182              throw new limit_invalid_parameter_exception(
 183                  "Limit must be between 1 and 50 (inclusive)");
 184          }
 185  
 186          $vault = \core_calendar\local\event\container::get_event_vault();
 187  
 188          $afterevent = null;
 189          if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
 190              $afterevent = $event;
 191          }
 192  
 193          return $vault->get_action_events_by_course(
 194              $USER, $course, $timesortfrom, $timesortto, $afterevent, $limitnum);
 195      }
 196  
 197      /**
 198       * Get a list of action events for the logged in user by the given
 199       * courses and timesort values.
 200       *
 201       * The limit number applies per course, not for the result set as a whole.
 202       * E.g. Requesting 3 courses with a limit of 10 will result in up to 30
 203       * events being returned (up to 10 per course).
 204       *
 205       * @param array $courses The courses the events must belong to
 206       * @param int|null $timesortfrom The start timesort value (inclusive)
 207       * @param int|null $timesortto The end timesort value (inclusive)
 208       * @param int $limitnum Limit results per course to this amount (between 1 and 50)
 209       * @return array A list of action_event_interface objects indexed by course id
 210       */
 211      public static function get_action_events_by_courses(
 212          $courses = [],
 213          $timesortfrom = null,
 214          $timesortto = null,
 215          $limitnum = 20
 216      ) {
 217          $return = [];
 218  
 219          foreach ($courses as $course) {
 220              $return[$course->id] = self::get_action_events_by_course(
 221                  $course,
 222                  $timesortfrom,
 223                  $timesortto,
 224                  null,
 225                  $limitnum
 226              );
 227          }
 228  
 229          return $return;
 230      }
 231  
 232      /**
 233       * Change the start day for an event. Only the date will be
 234       * modified, the time of day for the event will be left as is.
 235       *
 236       * @param event_interface $event The existing event to modify
 237       * @param DateTimeInterface $startdate The new date to use for the start day
 238       * @return event_interface The new event with updated start date
 239       */
 240      public static function update_event_start_day(
 241          event_interface $event,
 242          \DateTimeInterface $startdate
 243      ) {
 244          global $DB;
 245  
 246          $mapper = container::get_event_mapper();
 247          $legacyevent = $mapper->from_event_to_legacy_event($event);
 248          $hascoursemodule = !empty($event->get_course_module());
 249          $moduleinstance = null;
 250          $starttime = $event->get_times()->get_start_time()->setDate(
 251              $startdate->format('Y'),
 252              $startdate->format('n'),
 253              $startdate->format('j')
 254          );
 255          $starttimestamp = $starttime->getTimestamp();
 256  
 257          if ($hascoursemodule) {
 258              $moduleinstance = $DB->get_record(
 259                  $event->get_course_module()->get('modname'),
 260                  ['id' => $event->get_course_module()->get('instance')],
 261                  '*',
 262                  MUST_EXIST
 263              );
 264  
 265              // If there is a timestart range callback implemented then we can
 266              // use the values returned from the valid timestart range to apply
 267              // some default validation on the event's timestart value to ensure
 268              // that it falls within the specified range.
 269              list($min, $max) = component_callback(
 270                  'mod_' . $event->get_course_module()->get('modname'),
 271                  'core_calendar_get_valid_event_timestart_range',
 272                  [$legacyevent, $moduleinstance],
 273                  [false, false]
 274              );
 275          } else if ($legacyevent->courseid != 0 && $legacyevent->courseid != SITEID && $legacyevent->groupid == 0) {
 276              // This is a course event.
 277              list($min, $max) = component_callback(
 278                  'core_course',
 279                  'core_calendar_get_valid_event_timestart_range',
 280                  [$legacyevent, $event->get_course()->get_proxied_instance()],
 281                  [0, 0]
 282              );
 283          } else {
 284              $min = $max = 0;
 285          }
 286  
 287          // If the callback returns false for either value it means that
 288          // there is no valid time start range.
 289          if ($min === false || $max === false) {
 290              throw new \moodle_exception('The start day of this event can not be modified');
 291          }
 292  
 293          if ($min && $starttimestamp < $min[0]) {
 294              throw new \moodle_exception($min[1]);
 295          }
 296  
 297          if ($max && $starttimestamp > $max[0]) {
 298              throw new \moodle_exception($max[1]);
 299          }
 300  
 301          // This function does our capability checks.
 302          $legacyevent->update((object) ['timestart' => $starttime->getTimestamp()]);
 303  
 304          // Check that the user is allowed to manually edit calendar events before
 305          // calling the event updated callback. The manual flag causes the code to
 306          // check the user has the capabilities to modify the modules.
 307          //
 308          // We don't want to call the event update callback if the user isn't allowed
 309          // to modify course modules because depending on the callback it can make
 310          // some changes that would be considered security issues, such as updating the
 311          // due date for an assignment.
 312          if ($hascoursemodule && calendar_edit_event_allowed($legacyevent, true)) {
 313              // If this event is from an activity then we need to call
 314              // the activity callback to let it know that the event it
 315              // created has been modified so it needs to update accordingly.
 316              component_callback(
 317                  'mod_' . $event->get_course_module()->get('modname'),
 318                  'core_calendar_event_timestart_updated',
 319                  [$legacyevent, $moduleinstance]
 320              );
 321          }
 322  
 323          return $mapper->from_legacy_event_to_event($legacyevent);
 324      }
 325  }