| 1 | efrain | 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,
 | 
        
           | 1441 | ariadna | 71 |         ?array $usersfilter = null,
 | 
        
           |  |  | 72 |         ?array $groupsfilter = null,
 | 
        
           |  |  | 73 |         ?array $coursesfilter = null,
 | 
        
           |  |  | 74 |         ?array $categoriesfilter = null,
 | 
        
           | 1 | efrain | 75 |         $withduration = true,
 | 
        
           |  |  | 76 |         $ignorehidden = true,
 | 
        
           | 1441 | ariadna | 77 |         ?callable $filter = null
 | 
        
           | 1 | efrain | 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 | }
 |