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