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