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]

   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       * @param string|null $searchvalue The value a user wishes to search against
 124       * @return array A list of action_event_interface objects
 125       * @throws \moodle_exception
 126       */
 127      public static function get_action_events_by_timesort(
 128          $timesortfrom = null,
 129          $timesortto = null,
 130          $aftereventid = null,
 131          $limitnum = 20,
 132          $limittononsuspendedevents = false,
 133          ?\stdClass $user = null,
 134          ?string $searchvalue = null
 135      ) {
 136          global $USER;
 137  
 138          if (!$user) {
 139              $user = $USER;
 140          }
 141  
 142          if (is_null($timesortfrom) && is_null($timesortto)) {
 143              throw new \moodle_exception("Must provide a timesort to and/or from value");
 144          }
 145  
 146          if ($limitnum < 1 || $limitnum > 50) {
 147              throw new \moodle_exception("Limit must be between 1 and 50 (inclusive)");
 148          }
 149  
 150          \core_calendar\local\event\container::set_requesting_user($user->id);
 151          $vault = \core_calendar\local\event\container::get_event_vault();
 152  
 153          $afterevent = null;
 154          if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
 155              $afterevent = $event;
 156          }
 157  
 158          return $vault->get_action_events_by_timesort($user, $timesortfrom, $timesortto, $afterevent, $limitnum,
 159                  $limittononsuspendedevents, $searchvalue);
 160      }
 161  
 162      /**
 163       * Get a list of action events for the logged in user by the given
 164       * course and timesort values.
 165       *
 166       * @param \stdClass $course The course the events must belong to
 167       * @param int|null $timesortfrom The start timesort value (inclusive)
 168       * @param int|null $timesortto The end timesort value (inclusive)
 169       * @param int|null $aftereventid Only return events after this one
 170       * @param int $limitnum Limit results to this amount (between 1 and 50)
 171       * @param string|null $searchvalue The value a user wishes to search against
 172       * @return array A list of action_event_interface objects
 173       * @throws limit_invalid_parameter_exception
 174       */
 175      public static function get_action_events_by_course(
 176          $course,
 177          $timesortfrom = null,
 178          $timesortto = null,
 179          $aftereventid = null,
 180          $limitnum = 20,
 181          ?string $searchvalue = null
 182      ) {
 183          global $USER;
 184  
 185          if ($limitnum < 1 || $limitnum > 50) {
 186              throw new limit_invalid_parameter_exception(
 187                  "Limit must be between 1 and 50 (inclusive)");
 188          }
 189  
 190          $vault = \core_calendar\local\event\container::get_event_vault();
 191  
 192          $afterevent = null;
 193          if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
 194              $afterevent = $event;
 195          }
 196  
 197          return $vault->get_action_events_by_course(
 198              $USER, $course, $timesortfrom, $timesortto, $afterevent, $limitnum, $searchvalue);
 199      }
 200  
 201      /**
 202       * Get a list of action events for the logged in user by the given
 203       * courses and timesort values.
 204       *
 205       * The limit number applies per course, not for the result set as a whole.
 206       * E.g. Requesting 3 courses with a limit of 10 will result in up to 30
 207       * events being returned (up to 10 per course).
 208       *
 209       * @param array $courses The courses the events must belong to
 210       * @param int|null $timesortfrom The start timesort value (inclusive)
 211       * @param int|null $timesortto The end timesort value (inclusive)
 212       * @param int $limitnum Limit results per course to this amount (between 1 and 50)
 213       * @param string|null $searchvalue The value a user wishes to search against
 214       * @return array A list of action_event_interface objects indexed by course id
 215       */
 216      public static function get_action_events_by_courses(
 217          $courses = [],
 218          $timesortfrom = null,
 219          $timesortto = null,
 220          $limitnum = 20,
 221          ?string $searchvalue = null
 222      ) {
 223          $return = [];
 224  
 225          foreach ($courses as $course) {
 226              $return[$course->id] = self::get_action_events_by_course(
 227                  $course,
 228                  $timesortfrom,
 229                  $timesortto,
 230                  null,
 231                  $limitnum,
 232                  $searchvalue
 233              );
 234          }
 235  
 236          return $return;
 237      }
 238  
 239      /**
 240       * Change the start day for an event. Only the date will be
 241       * modified, the time of day for the event will be left as is.
 242       *
 243       * @param event_interface $event The existing event to modify
 244       * @param DateTimeInterface $startdate The new date to use for the start day
 245       * @return event_interface The new event with updated start date
 246       */
 247      public static function update_event_start_day(
 248          event_interface $event,
 249          \DateTimeInterface $startdate
 250      ) {
 251          global $DB;
 252  
 253          $mapper = container::get_event_mapper();
 254          $legacyevent = $mapper->from_event_to_legacy_event($event);
 255          $hascoursemodule = !empty($event->get_course_module());
 256          $moduleinstance = null;
 257          $starttime = $event->get_times()->get_start_time()->setDate(
 258              $startdate->format('Y'),
 259              $startdate->format('n'),
 260              $startdate->format('j')
 261          );
 262          $starttimestamp = $starttime->getTimestamp();
 263  
 264          if ($hascoursemodule) {
 265              $moduleinstance = $DB->get_record(
 266                  $event->get_course_module()->get('modname'),
 267                  ['id' => $event->get_course_module()->get('instance')],
 268                  '*',
 269                  MUST_EXIST
 270              );
 271  
 272              // If there is a timestart range callback implemented then we can
 273              // use the values returned from the valid timestart range to apply
 274              // some default validation on the event's timestart value to ensure
 275              // that it falls within the specified range.
 276              list($min, $max) = component_callback(
 277                  'mod_' . $event->get_course_module()->get('modname'),
 278                  'core_calendar_get_valid_event_timestart_range',
 279                  [$legacyevent, $moduleinstance],
 280                  [false, false]
 281              );
 282          } else if ($legacyevent->courseid != 0 && $legacyevent->courseid != SITEID && $legacyevent->groupid == 0) {
 283              // This is a course event.
 284              list($min, $max) = component_callback(
 285                  'core_course',
 286                  'core_calendar_get_valid_event_timestart_range',
 287                  [$legacyevent, $event->get_course()->get_proxied_instance()],
 288                  [0, 0]
 289              );
 290          } else {
 291              $min = $max = 0;
 292          }
 293  
 294          // If the callback returns false for either value it means that
 295          // there is no valid time start range.
 296          if ($min === false || $max === false) {
 297              throw new \moodle_exception('The start day of this event can not be modified');
 298          }
 299  
 300          if ($min && $starttimestamp < $min[0]) {
 301              throw new \moodle_exception($min[1]);
 302          }
 303  
 304          if ($max && $starttimestamp > $max[0]) {
 305              throw new \moodle_exception($max[1]);
 306          }
 307  
 308          // This function does our capability checks.
 309          $legacyevent->update((object) ['timestart' => $starttime->getTimestamp()]);
 310  
 311          // Check that the user is allowed to manually edit calendar events before
 312          // calling the event updated callback. The manual flag causes the code to
 313          // check the user has the capabilities to modify the modules.
 314          //
 315          // We don't want to call the event update callback if the user isn't allowed
 316          // to modify course modules because depending on the callback it can make
 317          // some changes that would be considered security issues, such as updating the
 318          // due date for an assignment.
 319          if ($hascoursemodule && calendar_edit_event_allowed($legacyevent, true)) {
 320              // If this event is from an activity then we need to call
 321              // the activity callback to let it know that the event it
 322              // created has been modified so it needs to update accordingly.
 323              component_callback(
 324                  'mod_' . $event->get_course_module()->get('modname'),
 325                  'core_calendar_event_timestart_updated',
 326                  [$legacyevent, $moduleinstance]
 327              );
 328  
 329              // Rebuild the course cache to make sure the updated dates are reflected.
 330              $courseid = $event->get_course()->get('id');
 331              $cmid = $event->get_course_module()->get('id');
 332              \course_modinfo::purge_course_module_cache($courseid, $cmid);
 333              rebuild_course_cache($courseid, true, true);
 334          }
 335  
 336          return $mapper->from_legacy_event_to_event($legacyevent);
 337      }
 338  }