MDL-64063 core_calendar: events by time considers enrolment status.
[moodle.git] / calendar / classes / local / api.php
CommitLineData
392d6a49
RW
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
25namespace core_calendar\local;
26
27defined('MOODLE_INTERNAL') || die();
28
55f5fcb9
RW
29use core_calendar\local\event\container;
30use core_calendar\local\event\entities\event_interface;
e62cd85f
RW
31use core_calendar\local\event\exceptions\limit_invalid_parameter_exception;
32
392d6a49
RW
33/**
34 * Class containing the local calendar API.
35 *
2229368a
MN
36 * This should not be used outside of core_calendar.
37 *
392d6a49
RW
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 */
42class api {
258a5705
CB
43 /**
44 * Get all events restricted by various parameters, taking in to account user and group overrides.
45 *
20592f5f
JP
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.
42e76c3f 60 * @return \core_calendar\local\event\entities\event_interface[] Array of event_interfaces.
258a5705
CB
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,
0085b0ea 74 array $categoriesfilter = null,
258a5705 75 $withduration = true,
7fe41af5
AN
76 $ignorehidden = true,
77 callable $filter = null
258a5705
CB
78 ) {
79 global $USER;
80
d10693cb 81 $vault = \core_calendar\local\event\container::get_event_vault();
258a5705
CB
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,
0085b0ea 106 $categoriesfilter,
258a5705 107 $withduration,
7fe41af5
AN
108 $ignorehidden,
109 $filter
258a5705
CB
110 );
111 }
392d6a49
RW
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)
644ffbd3 121 * @param bool $lmittononsuspendedevents Limit course events to courses the user is active in (not suspended).
392d6a49 122 * @return array A list of action_event_interface objects
20592f5f 123 * @throws \moodle_exception
392d6a49
RW
124 */
125 public static function get_action_events_by_timesort(
126 $timesortfrom = null,
127 $timesortto = null,
128 $aftereventid = null,
644ffbd3
AG
129 $limitnum = 20,
130 $limittononsuspendedevents = false
392d6a49
RW
131 ) {
132 global $USER;
133
134 if (is_null($timesortfrom) && is_null($timesortto)) {
135 throw new \moodle_exception("Must provide a timesort to and/or from value");
136 }
137
138 if ($limitnum < 1 || $limitnum > 50) {
139 throw new \moodle_exception("Limit must be between 1 and 50 (inclusive)");
140 }
141
d10693cb 142 $vault = \core_calendar\local\event\container::get_event_vault();
392d6a49
RW
143
144 $afterevent = null;
145 if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
146 $afterevent = $event;
147 }
148
644ffbd3
AG
149 return $vault->get_action_events_by_timesort($USER, $timesortfrom, $timesortto, $afterevent, $limitnum,
150 $limittononsuspendedevents);
392d6a49 151 }
e62cd85f
RW
152
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
20592f5f 163 * @throws limit_invalid_parameter_exception
e62cd85f
RW
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;
173
174 if ($limitnum < 1 || $limitnum > 50) {
175 throw new limit_invalid_parameter_exception(
176 "Limit must be between 1 and 50 (inclusive)");
177 }
178
d10693cb 179 $vault = \core_calendar\local\event\container::get_event_vault();
e62cd85f
RW
180
181 $afterevent = null;
182 if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
183 $afterevent = $event;
184 }
185
186 return $vault->get_action_events_by_course(
187 $USER, $course, $timesortfrom, $timesortto, $afterevent, $limitnum);
188 }
8a082024
RW
189
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 = [];
211
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 }
221
222 return $return;
223 }
55f5fcb9
RW
224
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
96283892 230 * @param DateTimeInterface $startdate The new date to use for the start day
55f5fcb9
RW
231 * @return event_interface The new event with updated start date
232 */
233 public static function update_event_start_day(
234 event_interface $event,
96283892 235 \DateTimeInterface $startdate
55f5fcb9 236 ) {
478b1d19
RW
237 global $DB;
238
55f5fcb9
RW
239 $mapper = container::get_event_mapper();
240 $legacyevent = $mapper->from_event_to_legacy_event($event);
39fe5929 241 $hascoursemodule = !empty($event->get_course_module());
478b1d19 242 $moduleinstance = null;
55f5fcb9 243 $starttime = $event->get_times()->get_start_time()->setDate(
96283892
RW
244 $startdate->format('Y'),
245 $startdate->format('n'),
246 $startdate->format('j')
55f5fcb9 247 );
5974bfeb 248 $starttimestamp = $starttime->getTimestamp();
55f5fcb9 249
39fe5929 250 if ($hascoursemodule) {
478b1d19
RW
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 );
478b1d19
RW
257
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(
39fe5929 263 'mod_' . $event->get_course_module()->get('modname'),
478b1d19
RW
264 'core_calendar_get_valid_event_timestart_range',
265 [$legacyevent, $moduleinstance],
6688ae2b 266 [false, false]
39fe5929 267 );
5974bfeb
SR
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 }
478b1d19 279
5974bfeb
SR
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 }
6688ae2b 285
5974bfeb
SR
286 if ($min && $starttimestamp < $min[0]) {
287 throw new \moodle_exception($min[1]);
288 }
478b1d19 289
5974bfeb
SR
290 if ($max && $starttimestamp > $max[0]) {
291 throw new \moodle_exception($max[1]);
39fe5929
RW
292 }
293
55f5fcb9
RW
294 // This function does our capability checks.
295 $legacyevent->update((object) ['timestart' => $starttime->getTimestamp()]);
296
39fe5929
RW
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
478b1d19 304 // due date for an assignment.
39fe5929
RW
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',
478b1d19 312 [$legacyevent, $moduleinstance]
39fe5929
RW
313 );
314 }
315
55f5fcb9
RW
316 return $mapper->from_legacy_event_to_event($legacyevent);
317 }
392d6a49 318}