MDL-58424 core: undeprecated calendar_* functions
[moodle.git] / calendar / lib.php
CommitLineData
93c91ee4 1<?php
08b4a4e1
RW
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/>.
7423f116 16
08b4a4e1
RW
17/**
18 * Calendar extension
19 *
20 * @package core_calendar
21 * @copyright 2004 Greek School Network (http://www.sch.gr), Jon Papaioannou,
22 * Avgoustos Tsinakos
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
e30390a0
SH
26if (!defined('MOODLE_INTERNAL')) {
27 die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
28}
b5a52acd 29
08b4a4e1
RW
30/**
31 * These are read by the administration component to provide default values
32 */
33
34/**
35 * CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD - default value of upcoming event preference
36 */
bb4a2e85 37define('CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD', 21);
08b4a4e1
RW
38
39/**
40 * CALENDAR_DEFAULT_UPCOMING_MAXEVENTS - default value to display the maximum number of upcoming event
41 */
bb4a2e85 42define('CALENDAR_DEFAULT_UPCOMING_MAXEVENTS', 10);
08b4a4e1
RW
43
44/**
45 * CALENDAR_DEFAULT_STARTING_WEEKDAY - default value to display the starting weekday
46 */
47define('CALENDAR_DEFAULT_STARTING_WEEKDAY', 1);
48
bb4a2e85 49// This is a packed bitfield: day X is "weekend" if $field & (1 << X) is true
50// Default value = 65 = 64 + 1 = 2^6 + 2^0 = Saturday & Sunday
08b4a4e1
RW
51
52/**
53 * CALENDAR_DEFAULT_WEEKEND - default value for weekend (Saturday & Sunday)
54 */
55define('CALENDAR_DEFAULT_WEEKEND', 65);
56
57/**
58 * CALENDAR_URL - path to calendar's folder
59 */
76d9df3f 60define('CALENDAR_URL', $CFG->wwwroot.'/calendar/');
08b4a4e1
RW
61
62/**
63 * CALENDAR_TF_24 - Calendar time in 24 hours format
64 */
76d9df3f 65define('CALENDAR_TF_24', '%H:%M');
08b4a4e1
RW
66
67/**
68 * CALENDAR_TF_12 - Calendar time in 12 hours format
69 */
76d9df3f 70define('CALENDAR_TF_12', '%I:%M %p');
bb4a2e85 71
08b4a4e1
RW
72/**
73 * CALENDAR_EVENT_GLOBAL - Global calendar event types
74 */
797cedc7 75define('CALENDAR_EVENT_GLOBAL', 1);
08b4a4e1
RW
76
77/**
78 * CALENDAR_EVENT_COURSE - Course calendar event types
79 */
797cedc7 80define('CALENDAR_EVENT_COURSE', 2);
08b4a4e1
RW
81
82/**
83 * CALENDAR_EVENT_GROUP - group calendar event types
84 */
797cedc7 85define('CALENDAR_EVENT_GROUP', 4);
08b4a4e1
RW
86
87/**
88 * CALENDAR_EVENT_USER - user calendar event types
89 */
797cedc7
SH
90define('CALENDAR_EVENT_USER', 8);
91
7423f116 92
b5a52acd
JH
93/**
94 * CALENDAR_IMPORT_FROM_FILE - import the calendar from a file
95 */
96define('CALENDAR_IMPORT_FROM_FILE', 0);
97
98/**
99 * CALENDAR_IMPORT_FROM_URL - import the calendar from a URL
100 */
101define('CALENDAR_IMPORT_FROM_URL', 1);
102
103/**
104 * CALENDAR_IMPORT_EVENT_UPDATED - imported event was updated
105 */
106define('CALENDAR_IMPORT_EVENT_UPDATED', 1);
107
108/**
109 * CALENDAR_IMPORT_EVENT_INSERTED - imported event was added by insert
110 */
111define('CALENDAR_IMPORT_EVENT_INSERTED', 2);
112
ee74a2a1
AA
113/**
114 * CALENDAR_SUBSCRIPTION_UPDATE - Used to represent update action for subscriptions in various forms.
115 */
116define('CALENDAR_SUBSCRIPTION_UPDATE', 1);
117
118/**
119 * CALENDAR_SUBSCRIPTION_REMOVE - Used to represent remove action for subscriptions in various forms.
120 */
121define('CALENDAR_SUBSCRIPTION_REMOVE', 2);
122
ca75ec4f
JP
123/**
124 * CALENDAR_EVENT_USER_OVERRIDE_PRIORITY - Constant for the user override priority.
125 */
126define('CALENDAR_EVENT_USER_OVERRIDE_PRIORITY', 9999999);
127
5ca71c2d
CB
128/**
129 * CALENDAR_EVENT_TYPE_STANDARD - Standard events.
130 */
131define('CALENDAR_EVENT_TYPE_STANDARD', 0);
132
133/**
134 * CALENDAR_EVENT_TYPE_ACTION - Action events.
135 */
136define('CALENDAR_EVENT_TYPE_ACTION', 1);
137
e1cd93ce
MN
138/**
139 * Manage calendar events.
140 *
141 * This class provides the required functionality in order to manage calendar events.
142 * It was introduced as part of Moodle 2.0 and was created in order to provide a
143 * better framework for dealing with calendar events in particular regard to file
144 * handling through the new file API.
145 *
146 * @package core_calendar
147 * @category calendar
148 * @copyright 2009 Sam Hemelryk
149 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
150 *
151 * @property int $id The id within the event table
152 * @property string $name The name of the event
153 * @property string $description The description of the event
154 * @property int $format The format of the description FORMAT_?
155 * @property int $courseid The course the event is associated with (0 if none)
156 * @property int $groupid The group the event is associated with (0 if none)
157 * @property int $userid The user the event is associated with (0 if none)
158 * @property int $repeatid If this is a repeated event this will be set to the
159 * id of the original
160 * @property string $modulename If added by a module this will be the module name
161 * @property int $instance If added by a module this will be the module instance
162 * @property string $eventtype The event type
163 * @property int $timestart The start time as a timestamp
164 * @property int $timeduration The duration of the event in seconds
165 * @property int $visible 1 if the event is visible
166 * @property int $uuid ?
167 * @property int $sequence ?
168 * @property int $timemodified The time last modified as a timestamp
169 */
170class calendar_event {
171
172 /** @var array An object containing the event properties can be accessed via the magic __get/set methods */
173 protected $properties = null;
174
175 /** @var string The converted event discription with file paths resolved.
176 * This gets populated when someone requests description for the first time */
177 protected $_description = null;
178
179 /** @var array The options to use with this description editor */
180 protected $editoroptions = array(
181 'subdirs' => false,
182 'forcehttps' => false,
183 'maxfiles' => -1,
184 'maxbytes' => null,
185 'trusttext' => false);
186
187 /** @var object The context to use with the description editor */
188 protected $editorcontext = null;
189
190 /**
191 * Instantiates a new event and optionally populates its properties with the data provided.
192 *
193 * @param \stdClass $data Optional. An object containing the properties to for
194 * an event
195 */
196 public function __construct($data = null) {
197 global $CFG, $USER;
198
199 // First convert to object if it is not already (should either be object or assoc array).
200 if (!is_object($data)) {
201 $data = (object) $data;
202 }
203
204 $this->editoroptions['maxbytes'] = $CFG->maxbytes;
205
206 $data->eventrepeats = 0;
207
208 if (empty($data->id)) {
209 $data->id = null;
210 }
211
212 if (!empty($data->subscriptionid)) {
213 $data->subscription = \core_calendar\api::get_subscription($data->subscriptionid);
214 }
215
216 // Default to a user event.
217 if (empty($data->eventtype)) {
218 $data->eventtype = 'user';
219 }
220
221 // Default to the current user.
222 if (empty($data->userid)) {
223 $data->userid = $USER->id;
224 }
225
226 if (!empty($data->timeduration) && is_array($data->timeduration)) {
227 $data->timeduration = make_timestamp(
228 $data->timeduration['year'], $data->timeduration['month'], $data->timeduration['day'],
229 $data->timeduration['hour'], $data->timeduration['minute']) - $data->timestart;
230 }
231
232 if (!empty($data->description) && is_array($data->description)) {
233 $data->format = $data->description['format'];
234 $data->description = $data->description['text'];
235 } else if (empty($data->description)) {
236 $data->description = '';
237 $data->format = editors_get_preferred_format();
238 }
239
240 // Ensure form is defaulted correctly.
241 if (empty($data->format)) {
242 $data->format = editors_get_preferred_format();
243 }
244
245 $this->properties = $data;
246
247 if (empty($data->context)) {
248 $this->properties->context = $this->calculate_context();
249 }
250 }
251
252 /**
253 * Magic set method.
254 *
255 * Attempts to call a set_$key method if one exists otherwise falls back
256 * to simply set the property.
257 *
258 * @param string $key property name
259 * @param mixed $value value of the property
260 */
261 public function __set($key, $value) {
262 if (method_exists($this, 'set_'.$key)) {
263 $this->{'set_'.$key}($value);
264 }
265 $this->properties->{$key} = $value;
266 }
267
268 /**
269 * Magic get method.
270 *
271 * Attempts to call a get_$key method to return the property and ralls over
272 * to return the raw property.
273 *
274 * @param string $key property name
275 * @return mixed property value
276 * @throws \coding_exception
277 */
278 public function __get($key) {
279 if (method_exists($this, 'get_'.$key)) {
280 return $this->{'get_'.$key}();
281 }
2a8e41b9 282 if (!property_exists($this->properties, $key)) {
e1cd93ce
MN
283 throw new \coding_exception('Undefined property requested');
284 }
285 return $this->properties->{$key};
286 }
287
288 /**
289 * Magic isset method.
290 *
291 * PHP needs an isset magic method if you use the get magic method and
292 * still want empty calls to work.
293 *
294 * @param string $key $key property name
295 * @return bool|mixed property value, false if property is not exist
296 */
297 public function __isset($key) {
298 return !empty($this->properties->{$key});
299 }
300
301 /**
302 * Calculate the context value needed for an event.
303 *
304 * Event's type can be determine by the available value store in $data
305 * It is important to check for the existence of course/courseid to determine
306 * the course event.
307 * Default value is set to CONTEXT_USER
308 *
309 * @return \stdClass The context object.
310 */
311 protected function calculate_context() {
312 global $USER, $DB;
313
314 $context = null;
315 if (isset($this->properties->courseid) && $this->properties->courseid > 0) {
316 $context = \context_course::instance($this->properties->courseid);
317 } else if (isset($this->properties->course) && $this->properties->course > 0) {
318 $context = \context_course::instance($this->properties->course);
319 } else if (isset($this->properties->groupid) && $this->properties->groupid > 0) {
320 $group = $DB->get_record('groups', array('id' => $this->properties->groupid));
321 $context = \context_course::instance($group->courseid);
322 } else if (isset($this->properties->userid) && $this->properties->userid > 0
323 && $this->properties->userid == $USER->id) {
324 $context = \context_user::instance($this->properties->userid);
325 } else if (isset($this->properties->userid) && $this->properties->userid > 0
326 && $this->properties->userid != $USER->id &&
327 isset($this->properties->instance) && $this->properties->instance > 0) {
328 $cm = get_coursemodule_from_instance($this->properties->modulename, $this->properties->instance, 0,
329 false, MUST_EXIST);
330 $context = \context_course::instance($cm->course);
331 } else {
332 $context = \context_user::instance($this->properties->userid);
333 }
334
335 return $context;
336 }
337
338 /**
339 * Returns an array of editoroptions for this event.
340 *
341 * @return array event editor options
342 */
343 protected function get_editoroptions() {
344 return $this->editoroptions;
345 }
346
347 /**
348 * Returns an event description: Called by __get
349 * Please use $blah = $event->description;
350 *
351 * @return string event description
352 */
353 protected function get_description() {
354 global $CFG;
355
356 require_once($CFG->libdir . '/filelib.php');
357
358 if ($this->_description === null) {
359 // Check if we have already resolved the context for this event.
360 if ($this->editorcontext === null) {
361 // Switch on the event type to decide upon the appropriate context to use for this event.
362 $this->editorcontext = $this->properties->context;
363 if ($this->properties->eventtype != 'user' && $this->properties->eventtype != 'course'
364 && $this->properties->eventtype != 'site' && $this->properties->eventtype != 'group') {
365 return clean_text($this->properties->description, $this->properties->format);
366 }
367 }
368
369 // Work out the item id for the editor, if this is a repeated event
370 // then the files will be associated with the original.
371 if (!empty($this->properties->repeatid) && $this->properties->repeatid > 0) {
372 $itemid = $this->properties->repeatid;
373 } else {
374 $itemid = $this->properties->id;
375 }
376
377 // Convert file paths in the description so that things display correctly.
378 $this->_description = file_rewrite_pluginfile_urls($this->properties->description, 'pluginfile.php',
379 $this->editorcontext->id, 'calendar', 'event_description', $itemid);
380 // Clean the text so no nasties get through.
381 $this->_description = clean_text($this->_description, $this->properties->format);
382 }
383
384 // Finally return the description.
385 return $this->_description;
386 }
387
388 /**
389 * Return the number of repeat events there are in this events series.
390 *
391 * @return int number of event repeated
392 */
393 public function count_repeats() {
394 global $DB;
395 if (!empty($this->properties->repeatid)) {
396 $this->properties->eventrepeats = $DB->count_records('event',
397 array('repeatid' => $this->properties->repeatid));
398 // We don't want to count ourselves.
399 $this->properties->eventrepeats--;
400 }
401 return $this->properties->eventrepeats;
402 }
403
404 /**
405 * Update or create an event within the database
406 *
407 * Pass in a object containing the event properties and this function will
408 * insert it into the database and deal with any associated files
409 *
410 * @see self::create()
411 * @see self::update()
412 *
413 * @param \stdClass $data object of event
414 * @param bool $checkcapability if moodle should check calendar managing capability or not
415 * @return bool event updated
416 */
417 public function update($data, $checkcapability=true) {
418 global $DB, $USER;
419
420 foreach ($data as $key => $value) {
421 $this->properties->$key = $value;
422 }
423
424 $this->properties->timemodified = time();
425 $usingeditor = (!empty($this->properties->description) && is_array($this->properties->description));
426
427 // Prepare event data.
428 $eventargs = array(
429 'context' => $this->properties->context,
430 'objectid' => $this->properties->id,
431 'other' => array(
432 'repeatid' => empty($this->properties->repeatid) ? 0 : $this->properties->repeatid,
433 'timestart' => $this->properties->timestart,
434 'name' => $this->properties->name
435 )
436 );
437
438 if (empty($this->properties->id) || $this->properties->id < 1) {
439
440 if ($checkcapability) {
441 if (!\core_calendar\api::can_add_event($this->properties)) {
442 print_error('nopermissiontoupdatecalendar');
443 }
444 }
445
446 if ($usingeditor) {
447 switch ($this->properties->eventtype) {
448 case 'user':
449 $this->properties->courseid = 0;
450 $this->properties->course = 0;
451 $this->properties->groupid = 0;
452 $this->properties->userid = $USER->id;
453 break;
454 case 'site':
455 $this->properties->courseid = SITEID;
456 $this->properties->course = SITEID;
457 $this->properties->groupid = 0;
458 $this->properties->userid = $USER->id;
459 break;
460 case 'course':
461 $this->properties->groupid = 0;
462 $this->properties->userid = $USER->id;
463 break;
464 case 'group':
465 $this->properties->userid = $USER->id;
466 break;
467 default:
468 // We should NEVER get here, but just incase we do lets fail gracefully.
469 $usingeditor = false;
470 break;
471 }
472
473 // If we are actually using the editor, we recalculate the context because some default values
474 // were set when calculate_context() was called from the constructor.
475 if ($usingeditor) {
476 $this->properties->context = $this->calculate_context();
477 $this->editorcontext = $this->properties->context;
478 }
479
480 $editor = $this->properties->description;
481 $this->properties->format = $this->properties->description['format'];
482 $this->properties->description = $this->properties->description['text'];
483 }
484
485 // Insert the event into the database.
486 $this->properties->id = $DB->insert_record('event', $this->properties);
487
488 if ($usingeditor) {
489 $this->properties->description = file_save_draft_area_files(
490 $editor['itemid'],
491 $this->editorcontext->id,
492 'calendar',
493 'event_description',
494 $this->properties->id,
495 $this->editoroptions,
496 $editor['text'],
497 $this->editoroptions['forcehttps']);
498 $DB->set_field('event', 'description', $this->properties->description,
499 array('id' => $this->properties->id));
500 }
501
502 // Log the event entry.
503 $eventargs['objectid'] = $this->properties->id;
504 $eventargs['context'] = $this->properties->context;
505 $event = \core\event\calendar_event_created::create($eventargs);
506 $event->trigger();
507
508 $repeatedids = array();
509
510 if (!empty($this->properties->repeat)) {
511 $this->properties->repeatid = $this->properties->id;
512 $DB->set_field('event', 'repeatid', $this->properties->repeatid, array('id' => $this->properties->id));
513
514 $eventcopy = clone($this->properties);
515 unset($eventcopy->id);
516
517 $timestart = new \DateTime('@' . $eventcopy->timestart);
518 $timestart->setTimezone(\core_date::get_user_timezone_object());
519
520 for ($i = 1; $i < $eventcopy->repeats; $i++) {
521
522 $timestart->add(new \DateInterval('P7D'));
523 $eventcopy->timestart = $timestart->getTimestamp();
524
525 // Get the event id for the log record.
526 $eventcopyid = $DB->insert_record('event', $eventcopy);
527
528 // If the context has been set delete all associated files.
529 if ($usingeditor) {
530 $fs = get_file_storage();
531 $files = $fs->get_area_files($this->editorcontext->id, 'calendar', 'event_description',
532 $this->properties->id);
533 foreach ($files as $file) {
534 $fs->create_file_from_storedfile(array('itemid' => $eventcopyid), $file);
535 }
536 }
537
538 $repeatedids[] = $eventcopyid;
539
540 // Trigger an event.
541 $eventargs['objectid'] = $eventcopyid;
542 $eventargs['other']['timestart'] = $eventcopy->timestart;
543 $event = \core\event\calendar_event_created::create($eventargs);
544 $event->trigger();
545 }
546 }
547
548 return true;
549 } else {
550
551 if ($checkcapability) {
552 if (!\core_calendar\api::can_edit_event($this->properties)) {
553 print_error('nopermissiontoupdatecalendar');
554 }
555 }
556
557 if ($usingeditor) {
558 if ($this->editorcontext !== null) {
559 $this->properties->description = file_save_draft_area_files(
560 $this->properties->description['itemid'],
561 $this->editorcontext->id,
562 'calendar',
563 'event_description',
564 $this->properties->id,
565 $this->editoroptions,
566 $this->properties->description['text'],
567 $this->editoroptions['forcehttps']);
568 } else {
569 $this->properties->format = $this->properties->description['format'];
570 $this->properties->description = $this->properties->description['text'];
571 }
572 }
573
574 $event = $DB->get_record('event', array('id' => $this->properties->id));
575
576 $updaterepeated = (!empty($this->properties->repeatid) && !empty($this->properties->repeateditall));
577
578 if ($updaterepeated) {
579 // Update all.
580 if ($this->properties->timestart != $event->timestart) {
581 $timestartoffset = $this->properties->timestart - $event->timestart;
582 $sql = "UPDATE {event}
583 SET name = ?,
584 description = ?,
585 timestart = timestart + ?,
586 timeduration = ?,
587 timemodified = ?
588 WHERE repeatid = ?";
589 $params = array($this->properties->name, $this->properties->description, $timestartoffset,
590 $this->properties->timeduration, time(), $event->repeatid);
591 } else {
592 $sql = "UPDATE {event} SET name = ?, description = ?, timeduration = ?, timemodified = ? WHERE repeatid = ?";
593 $params = array($this->properties->name, $this->properties->description,
594 $this->properties->timeduration, time(), $event->repeatid);
595 }
596 $DB->execute($sql, $params);
597
598 // Trigger an update event for each of the calendar event.
599 $events = $DB->get_records('event', array('repeatid' => $event->repeatid), '', '*');
600 foreach ($events as $calendarevent) {
601 $eventargs['objectid'] = $calendarevent->id;
602 $eventargs['other']['timestart'] = $calendarevent->timestart;
603 $event = \core\event\calendar_event_updated::create($eventargs);
604 $event->add_record_snapshot('event', $calendarevent);
605 $event->trigger();
606 }
607 } else {
608 $DB->update_record('event', $this->properties);
609 $event = self::load($this->properties->id);
610 $this->properties = $event->properties();
611
612 // Trigger an update event.
613 $event = \core\event\calendar_event_updated::create($eventargs);
614 $event->add_record_snapshot('event', $this->properties);
615 $event->trigger();
616 }
617
618 return true;
619 }
620 }
621
622 /**
623 * Deletes an event and if selected an repeated events in the same series
624 *
625 * This function deletes an event, any associated events if $deleterepeated=true,
626 * and cleans up any files associated with the events.
627 *
628 * @see self::delete()
629 *
630 * @param bool $deleterepeated delete event repeatedly
631 * @return bool succession of deleting event
632 */
633 public function delete($deleterepeated = false) {
634 global $DB;
635
636 // If $this->properties->id is not set then something is wrong.
637 if (empty($this->properties->id)) {
638 debugging('Attempting to delete an event before it has been loaded', DEBUG_DEVELOPER);
639 return false;
640 }
641 $calevent = $DB->get_record('event', array('id' => $this->properties->id), '*', MUST_EXIST);
642 // Delete the event.
643 $DB->delete_records('event', array('id' => $this->properties->id));
644
645 // Trigger an event for the delete action.
646 $eventargs = array(
647 'context' => $this->properties->context,
648 'objectid' => $this->properties->id,
649 'other' => array(
650 'repeatid' => empty($this->properties->repeatid) ? 0 : $this->properties->repeatid,
651 'timestart' => $this->properties->timestart,
652 'name' => $this->properties->name
653 ));
654 $event = \core\event\calendar_event_deleted::create($eventargs);
655 $event->add_record_snapshot('event', $calevent);
656 $event->trigger();
657
658 // If we are deleting parent of a repeated event series, promote the next event in the series as parent.
659 if (($this->properties->id == $this->properties->repeatid) && !$deleterepeated) {
660 $newparent = $DB->get_field_sql("SELECT id from {event} where repeatid = ? order by id ASC",
661 array($this->properties->id), IGNORE_MULTIPLE);
662 if (!empty($newparent)) {
663 $DB->execute("UPDATE {event} SET repeatid = ? WHERE repeatid = ?",
664 array($newparent, $this->properties->id));
665 // Get all records where the repeatid is the same as the event being removed.
666 $events = $DB->get_records('event', array('repeatid' => $newparent));
667 // For each of the returned events trigger an update event.
668 foreach ($events as $calendarevent) {
669 // Trigger an event for the update.
670 $eventargs['objectid'] = $calendarevent->id;
671 $eventargs['other']['timestart'] = $calendarevent->timestart;
672 $event = \core\event\calendar_event_updated::create($eventargs);
673 $event->add_record_snapshot('event', $calendarevent);
674 $event->trigger();
675 }
676 }
677 }
678
679 // If the editor context hasn't already been set then set it now.
680 if ($this->editorcontext === null) {
681 $this->editorcontext = $this->properties->context;
682 }
683
684 // If the context has been set delete all associated files.
685 if ($this->editorcontext !== null) {
686 $fs = get_file_storage();
687 $files = $fs->get_area_files($this->editorcontext->id, 'calendar', 'event_description', $this->properties->id);
688 foreach ($files as $file) {
689 $file->delete();
690 }
691 }
692
693 // If we need to delete repeated events then we will fetch them all and delete one by one.
694 if ($deleterepeated && !empty($this->properties->repeatid) && $this->properties->repeatid > 0) {
695 // Get all records where the repeatid is the same as the event being removed.
696 $events = $DB->get_records('event', array('repeatid' => $this->properties->repeatid));
697 // For each of the returned events populate an event object and call delete.
698 // make sure the arg passed is false as we are already deleting all repeats.
699 foreach ($events as $event) {
700 $event = new calendar_event($event);
701 $event->delete(false);
702 }
703 }
704
705 return true;
706 }
707
708 /**
709 * Fetch all event properties.
710 *
711 * This function returns all of the events properties as an object and optionally
712 * can prepare an editor for the description field at the same time. This is
713 * designed to work when the properties are going to be used to set the default
714 * values of a moodle forms form.
715 *
716 * @param bool $prepareeditor If set to true a editor is prepared for use with
717 * the mforms editor element. (for description)
718 * @return \stdClass Object containing event properties
719 */
720 public function properties($prepareeditor = false) {
721 global $DB;
722
723 // First take a copy of the properties. We don't want to actually change the
724 // properties or we'd forever be converting back and forwards between an
725 // editor formatted description and not.
726 $properties = clone($this->properties);
727 // Clean the description here.
728 $properties->description = clean_text($properties->description, $properties->format);
729
730 // If set to true we need to prepare the properties for use with an editor
731 // and prepare the file area.
732 if ($prepareeditor) {
733
734 // We may or may not have a property id. If we do then we need to work
735 // out the context so we can copy the existing files to the draft area.
736 if (!empty($properties->id)) {
737
738 if ($properties->eventtype === 'site') {
739 // Site context.
740 $this->editorcontext = $this->properties->context;
741 } else if ($properties->eventtype === 'user') {
742 // User context.
743 $this->editorcontext = $this->properties->context;
744 } else if ($properties->eventtype === 'group' || $properties->eventtype === 'course') {
745 // First check the course is valid.
746 $course = $DB->get_record('course', array('id' => $properties->courseid));
747 if (!$course) {
748 print_error('invalidcourse');
749 }
750 // Course context.
751 $this->editorcontext = $this->properties->context;
752 // We have a course and are within the course context so we had
753 // better use the courses max bytes value.
754 $this->editoroptions['maxbytes'] = $course->maxbytes;
755 } else {
756 // If we get here we have a custom event type as used by some
757 // modules. In this case the event will have been added by
758 // code and we won't need the editor.
759 $this->editoroptions['maxbytes'] = 0;
760 $this->editoroptions['maxfiles'] = 0;
761 }
762
763 if (empty($this->editorcontext) || empty($this->editorcontext->id)) {
764 $contextid = false;
765 } else {
766 // Get the context id that is what we really want.
767 $contextid = $this->editorcontext->id;
768 }
769 } else {
770
771 // If we get here then this is a new event in which case we don't need a
772 // context as there is no existing files to copy to the draft area.
773 $contextid = null;
774 }
775
776 // If the contextid === false we don't support files so no preparing
777 // a draft area.
778 if ($contextid !== false) {
779 // Just encase it has already been submitted.
780 $draftiddescription = file_get_submitted_draft_itemid('description');
781 // Prepare the draft area, this copies existing files to the draft area as well.
782 $properties->description = file_prepare_draft_area($draftiddescription, $contextid, 'calendar',
783 'event_description', $properties->id, $this->editoroptions, $properties->description);
784 } else {
785 $draftiddescription = 0;
786 }
787
788 // Structure the description field as the editor requires.
789 $properties->description = array('text' => $properties->description, 'format' => $properties->format,
790 'itemid' => $draftiddescription);
791 }
792
793 // Finally return the properties.
794 return $properties;
795 }
796
797 /**
798 * Toggles the visibility of an event
799 *
800 * @param null|bool $force If it is left null the events visibility is flipped,
801 * If it is false the event is made hidden, if it is true it
802 * is made visible.
803 * @return bool if event is successfully updated, toggle will be visible
804 */
805 public function toggle_visibility($force = null) {
806 global $DB;
807
808 // Set visible to the default if it is not already set.
809 if (empty($this->properties->visible)) {
810 $this->properties->visible = 1;
811 }
812
813 if ($force === true || ($force !== false && $this->properties->visible == 0)) {
814 // Make this event visible.
815 $this->properties->visible = 1;
816 } else {
817 // Make this event hidden.
818 $this->properties->visible = 0;
819 }
820
821 // Update the database to reflect this change.
822 $success = $DB->set_field('event', 'visible', $this->properties->visible, array('id' => $this->properties->id));
823 $calendarevent = $DB->get_record('event', array('id' => $this->properties->id), '*', MUST_EXIST);
824
825 // Prepare event data.
826 $eventargs = array(
827 'context' => $this->properties->context,
828 'objectid' => $this->properties->id,
829 'other' => array(
830 'repeatid' => empty($this->properties->repeatid) ? 0 : $this->properties->repeatid,
831 'timestart' => $this->properties->timestart,
832 'name' => $this->properties->name
833 )
834 );
835 $event = \core\event\calendar_event_updated::create($eventargs);
836 $event->add_record_snapshot('event', $calendarevent);
837 $event->trigger();
838
839 return $success;
840 }
841
842 /**
843 * Returns an event object when provided with an event id.
844 *
845 * This function makes use of MUST_EXIST, if the event id passed in is invalid
846 * it will result in an exception being thrown.
847 *
848 * @param int|object $param event object or event id
849 * @return calendar_event
850 */
851 public static function load($param) {
852 global $DB;
853 if (is_object($param)) {
854 $event = new calendar_event($param);
855 } else {
856 $event = $DB->get_record('event', array('id' => (int)$param), '*', MUST_EXIST);
857 $event = new calendar_event($event);
858 }
859 return $event;
860 }
861
862 /**
863 * Creates a new event and returns an event object
864 *
865 * @param \stdClass|array $properties An object containing event properties
866 * @param bool $checkcapability Check caps or not
867 * @throws \coding_exception
868 *
869 * @return calendar_event|bool The event object or false if it failed
870 */
871 public static function create($properties, $checkcapability = true) {
872 if (is_array($properties)) {
873 $properties = (object)$properties;
874 }
875 if (!is_object($properties)) {
876 throw new \coding_exception('When creating an event properties should be either an object or an assoc array');
877 }
878 $event = new calendar_event($properties);
879 if ($event->update($properties, $checkcapability)) {
880 return $event;
881 } else {
882 return false;
883 }
884 }
885
886 /**
887 * Format the text using the external API.
888 *
889 * This function should we used when text formatting is required in external functions.
890 *
891 * @return array an array containing the text formatted and the text format
892 */
893 public function format_external_text() {
894
895 if ($this->editorcontext === null) {
896 // Switch on the event type to decide upon the appropriate context to use for this event.
897 $this->editorcontext = $this->properties->context;
898
899 if ($this->properties->eventtype != 'user' && $this->properties->eventtype != 'course'
900 && $this->properties->eventtype != 'site' && $this->properties->eventtype != 'group') {
901 // We don't have a context here, do a normal format_text.
902 return external_format_text($this->properties->description, $this->properties->format, $this->editorcontext->id);
903 }
904 }
905
906 // Work out the item id for the editor, if this is a repeated event then the files will be associated with the original.
907 if (!empty($this->properties->repeatid) && $this->properties->repeatid > 0) {
908 $itemid = $this->properties->repeatid;
909 } else {
910 $itemid = $this->properties->id;
911 }
912
913 return external_format_text($this->properties->description, $this->properties->format, $this->editorcontext->id,
914 'calendar', 'event_description', $itemid);
915 }
916}
917
36dc3b71
SH
918/**
919 * Calendar information class
920 *
921 * This class is used simply to organise the information pertaining to a calendar
922 * and is used primarily to make information easily available.
08b4a4e1
RW
923 *
924 * @package core_calendar
925 * @category calendar
926 * @copyright 2010 Sam Hemelryk
927 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36dc3b71
SH
928 */
929class calendar_information {
08b4a4e1 930
da304137
MN
931 /**
932 * @var int The timestamp
933 *
934 * Rather than setting the day, month and year we will set a timestamp which will be able
935 * to be used by multiple calendars.
936 */
937 public $time;
36dc3b71 938
08b4a4e1 939 /** @var int A course id */
36dc3b71 940 public $courseid = null;
08b4a4e1
RW
941
942 /** @var array An array of courses */
36dc3b71 943 public $courses = array();
08b4a4e1
RW
944
945 /** @var array An array of groups */
36dc3b71 946 public $groups = array();
08b4a4e1
RW
947
948 /** @var array An array of users */
36dc3b71
SH
949 public $users = array();
950
951 /**
952 * Creates a new instance
953 *
08b4a4e1
RW
954 * @param int $day the number of the day
955 * @param int $month the number of the month
956 * @param int $year the number of the year
da304137
MN
957 * @param int $time the unixtimestamp representing the date we want to view, this is used instead of $calmonth
958 * and $calyear to support multiple calendars
36dc3b71 959 */
da304137
MN
960 public function __construct($day = 0, $month = 0, $year = 0, $time = 0) {
961 // If a day, month and year were passed then convert it to a timestamp. If these were passed
962 // then we can assume the day, month and year are passed as Gregorian, as no where in core
963 // should we be passing these values rather than the time. This is done for BC.
964 if (!empty($day) || !empty($month) || !empty($year)) {
965 $date = usergetdate(time());
966 if (empty($day)) {
967 $day = $date['mday'];
968 }
969 if (empty($month)) {
970 $month = $date['mon'];
971 }
972 if (empty($year)) {
973 $year = $date['year'];
974 }
975 if (checkdate($month, $day, $year)) {
976 $this->time = make_timestamp($year, $month, $day);
977 } else {
978 $this->time = time();
979 }
980 } else if (!empty($time)) {
981 $this->time = $time;
982 } else {
983 $this->time = time();
d8d8bdd9 984 }
36dc3b71
SH
985 }
986
797cedc7 987 /**
08b4a4e1 988 * Initialize calendar information
797cedc7 989 *
08b4a4e1 990 * @param stdClass $course object
797cedc7 991 * @param array $coursestoload An array of courses [$course->id => $course]
08b4a4e1 992 * @param bool $ignorefilters options to use filter
797cedc7
SH
993 */
994 public function prepare_for_view(stdClass $course, array $coursestoload, $ignorefilters = false) {
995 $this->courseid = $course->id;
996 $this->course = $course;
12cbce0a 997 list($courses, $group, $user) = \core_calendar\api::set_filters($coursestoload, $ignorefilters);
797cedc7
SH
998 $this->courses = $courses;
999 $this->groups = $group;
1000 $this->users = $user;
1001 }
1002
36dc3b71
SH
1003 /**
1004 * Ensures the date for the calendar is correct and either sets it to now
1005 * or throws a moodle_exception if not
1006 *
08b4a4e1
RW
1007 * @param bool $defaultonow use current time
1008 * @throws moodle_exception
1009 * @return bool validation of checkdate
36dc3b71
SH
1010 */
1011 public function checkdate($defaultonow = true) {
1012 if (!checkdate($this->month, $this->day, $this->year)) {
1013 if ($defaultonow) {
1014 $now = usergetdate(time());
1015 $this->day = intval($now['mday']);
1016 $this->month = intval($now['mon']);
1017 $this->year = intval($now['year']);
1018 return true;
1019 } else {
1020 throw new moodle_exception('invaliddate');
1021 }
1022 }
1023 return true;
1024 }
da304137 1025
36dc3b71
SH
1026 /**
1027 * Gets todays timestamp for the calendar
08b4a4e1
RW
1028 *
1029 * @return int today timestamp
36dc3b71
SH
1030 */
1031 public function timestamp_today() {
da304137 1032 return $this->time;
36dc3b71
SH
1033 }
1034 /**
1035 * Gets tomorrows timestamp for the calendar
08b4a4e1
RW
1036 *
1037 * @return int tomorrow timestamp
36dc3b71
SH
1038 */
1039 public function timestamp_tomorrow() {
a0ef87de 1040 return strtotime('+1 day', $this->time);
36dc3b71
SH
1041 }
1042 /**
e30390a0 1043 * Adds the pretend blocks for the calendar
36dc3b71
SH
1044 *
1045 * @param core_calendar_renderer $renderer
08b4a4e1
RW
1046 * @param bool $showfilters display filters, false is set as default
1047 * @param string|null $view preference view options (eg: day, month, upcoming)
36dc3b71
SH
1048 */
1049 public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) {
1050 if ($showfilters) {
1051 $filters = new block_contents();
da304137 1052 $filters->content = $renderer->fake_block_filters($this->courseid, 0, 0, 0, $view, $this->courses);
36dc3b71
SH
1053 $filters->footer = '';
1054 $filters->title = get_string('eventskey', 'calendar');
1055 $renderer->add_pretend_calendar_block($filters, BLOCK_POS_RIGHT);
1056 }
1057 $block = new block_contents;
1058 $block->content = $renderer->fake_block_threemonths($this);
1059 $block->footer = '';
1060 $block->title = get_string('monthlyview', 'calendar');
1061 $renderer->add_pretend_calendar_block($block, BLOCK_POS_RIGHT);
1062 }
1d5bd3d2 1063}
b5a52acd 1064
10515e15
MN
1065/**
1066 * Get calendar events.
1067 *
1068 * @param int $tstart Start time of time range for events
1069 * @param int $tend End time of time range for events
1070 * @param array|int|boolean $users array of users, user id or boolean for all/no user events
1071 * @param array|int|boolean $groups array of groups, group id or boolean for all/no group events
1072 * @param array|int|boolean $courses array of courses, course id or boolean for all/no course events
1073 * @param boolean $withduration whether only events starting within time range selected
1074 * or events in progress/already started selected as well
1075 * @param boolean $ignorehidden whether to select only visible events or all events
1076 * @return array $events of selected events or an empty array if there aren't any (or there was an error)
1077 */
1078function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration=true, $ignorehidden=true) {
1079 // We have a new implementation of this function in the calendar API class, which has slightly different behaviour
1080 // so the old implementation must remain here.
1081 global $DB;
1082 $params = array();
1083
1084 // Quick test.
1085 if (empty($users) && empty($groups) && empty($courses)) {
1086 return array();
1087 }
1088
1089 // Array of filter conditions. To be concatenated by the OR operator.
1090 $filters = [];
1091
1092 // User filter.
1093 if ((is_array($users) && !empty($users)) or is_numeric($users)) {
1094 // Events from a number of users.
1095 list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
1096 $filters[] = "(e.userid $insqlusers AND e.courseid = 0 AND e.groupid = 0)";
1097 $params = array_merge($params, $inparamsusers);
1098 } else if ($users === true) {
1099 // Events from ALL users.
1100 $filters[] = "(e.userid != 0 AND e.courseid = 0 AND e.groupid = 0)";
1101 }
1102
1103 // Boolean false (no users at all): We don't need to do anything.
1104 // Group filter.
1105 if ((is_array($groups) && !empty($groups)) or is_numeric($groups)) {
1106 // Events from a number of groups.
1107 list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
1108 $filters[] = "e.groupid $insqlgroups";
1109 $params = array_merge($params, $inparamsgroups);
1110 } else if ($groups === true) {
1111 // Events from ALL groups.
1112 $filters[] = "e.groupid != 0";
1113 }
1114
1115 // Boolean false (no groups at all): We don't need to do anything.
1116 // Course filter.
1117 if ((is_array($courses) && !empty($courses)) or is_numeric($courses)) {
1118 list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
1119 $filters[] = "(e.groupid = 0 AND e.courseid $insqlcourses)";
1120 $params = array_merge($params, $inparamscourses);
1121 } else if ($courses === true) {
1122 // Events from ALL courses.
1123 $filters[] = "(e.groupid = 0 AND e.courseid != 0)";
1124 }
1125
1126 // Security check: if, by now, we have NOTHING in $whereclause, then it means
1127 // that NO event-selecting clauses were defined. Thus, we won't be returning ANY
1128 // events no matter what. Allowing the code to proceed might return a completely
1129 // valid query with only time constraints, thus selecting ALL events in that time frame!
1130 if (empty($filters)) {
1131 return array();
1132 }
1133
1134 // Build our clause for the filters.
1135 $filterclause = implode(' OR ', $filters);
1136
1137 // Array of where conditions for our query. To be concatenated by the AND operator.
1138 $whereconditions = ["($filterclause)"];
1139
1140 // Time clause.
1141 if ($withduration) {
1142 $timeclause = "((e.timestart >= :tstart1 OR e.timestart + e.timeduration > :tstart2) AND e.timestart <= :tend)";
1143 $params['tstart1'] = $tstart;
1144 $params['tstart2'] = $tstart;
1145 $params['tend'] = $tend;
1146 } else {
1147 $timeclause = "(e.timestart >= :tstart AND e.timestart <= :tend)";
1148 $params['tstart'] = $tstart;
1149 $params['tend'] = $tend;
1150 }
1151 $whereconditions[] = $timeclause;
1152
1153 // Show visible only.
1154 if ($ignorehidden) {
1155 $whereconditions[] = "(e.visible = 1)";
1156 }
1157
1158 // Build the main query's WHERE clause.
1159 $whereclause = implode(' AND ', $whereconditions);
1160
1161 // Build SQL subquery and conditions for filtered events based on priorities.
1162 $subquerywhere = '';
1163 $subqueryconditions = [];
1164
1165 // Get the user's courses. Otherwise, get the default courses being shown by the calendar.
1166 $usercourses = \core_calendar\api::get_default_courses();
1167
1168 // Set calendar filters.
1169 list($usercourses, $usergroups, $user) = \core_calendar\api::set_filters($usercourses, true);
1170 $subqueryparams = [];
1171
1172 // Flag to indicate whether the query needs to exclude group overrides.
1173 $viewgroupsonly = false;
1174 if ($user) {
1175 // Set filter condition for the user's events.
1176 $subqueryconditions[] = "(ev.userid = :user AND ev.courseid = 0 AND ev.groupid = 0)";
1177 $subqueryparams['user'] = $user;
1178 foreach ($usercourses as $courseid) {
1179 if (has_capability('moodle/site:accessallgroups', context_course::instance($courseid))) {
1180 $usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
1181 if (count($usergroupmembership) == 0) {
1182 $viewgroupsonly = true;
1183 break;
1184 }
1185 }
1186 }
1187 }
1188
1189 // Set filter condition for the user's group events.
1190 if ($usergroups === true || $viewgroupsonly) {
1191 // Fetch group events, but not group overrides.
1192 $subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
1193 } else if (!empty($usergroups)) {
1194 // Fetch group events and group overrides.
1195 list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
1196 $subqueryconditions[] = "(ev.groupid $inusergroups)";
1197 $subqueryparams = array_merge($subqueryparams, $inusergroupparams);
1198 }
1199
1200 // Get courses to be used for the subquery.
1201 $subquerycourses = [];
1202 if (is_array($courses)) {
1203 $subquerycourses = $courses;
1204 } else if (is_numeric($courses)) {
1205 $subquerycourses[] = $courses;
1206 }
1207
1208 // Merge with user courses, if necessary.
1209 if (!empty($usercourses)) {
1210 $subquerycourses = array_merge($subquerycourses, $usercourses);
1211 // Make sure we remove duplicate values.
1212 $subquerycourses = array_unique($subquerycourses);
1213 }
1214
1215 // Set subquery filter condition for the courses.
1216 if (!empty($subquerycourses)) {
1217 list($incourses, $incoursesparams) = $DB->get_in_or_equal($subquerycourses, SQL_PARAMS_NAMED);
1218 $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses)";
1219 $subqueryparams = array_merge($subqueryparams, $incoursesparams);
1220 }
1221
1222 // Build the WHERE condition for the sub-query.
1223 if (!empty($subqueryconditions)) {
1224 $subquerywhere = 'WHERE ' . implode(" OR ", $subqueryconditions);
1225 }
1226
1227 // Merge subquery parameters to the parameters of the main query.
1228 if (!empty($subqueryparams)) {
1229 $params = array_merge($params, $subqueryparams);
1230 }
1231
1232 // Sub-query that fetches the list of unique events that were filtered based on priority.
1233 $subquery = "SELECT ev.modulename,
1234 ev.instance,
1235 ev.eventtype,
1236 MAX(ev.priority) as priority
1237 FROM {event} ev
1238 $subquerywhere
1239 GROUP BY ev.modulename, ev.instance, ev.eventtype";
1240
1241 // Build the main query.
1242 $sql = "SELECT e.*
1243 FROM {event} e
1244 INNER JOIN ($subquery) fe
1245 ON e.modulename = fe.modulename
1246 AND e.instance = fe.instance
1247 AND e.eventtype = fe.eventtype
1248 AND (e.priority = fe.priority OR (e.priority IS NULL AND fe.priority IS NULL))
1249 LEFT JOIN {modules} m
1250 ON e.modulename = m.name
1251 WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause
1252 ORDER BY e.timestart";
1253 $events = $DB->get_records_sql($sql, $params);
1254
1255 if ($events === false) {
1256 $events = array();
1257 }
1258
1259 return $events;
1260}
1261
1262/**
1263 * Return the days of the week.
1264 *
1265 * @return array array of days
1266 */
1267function calendar_get_days() {
1268 return \core_calendar\api::get_days();
1269}
1270
1271/**
1272 * Get the subscription from a given id.
1273 *
1274 * @since Moodle 2.5
1275 * @param int $id id of the subscription
1276 * @return stdClass Subscription record from DB
1277 * @throws moodle_exception for an invalid id
1278 */
1279function calendar_get_subscription($id) {
1280 return \core_calendar\api::get_subscription($id);
1281}
1282
1283/**
1284 * Gets the first day of the week.
1285 *
1286 * Used to be define('CALENDAR_STARTING_WEEKDAY', blah);
1287 *
1288 * @return int
1289 */
1290function calendar_get_starting_weekday() {
1291 return \core_calendar\api::get_starting_weekday();
1292}
1293
1294/**
1295 * Generates the HTML for a miniature calendar.
1296 *
1297 * @param array $courses list of course to list events from
1298 * @param array $groups list of group
1299 * @param array $users user's info
1300 * @param int|bool $calmonth calendar month in numeric, default is set to false
1301 * @param int|bool $calyear calendar month in numeric, default is set to false
1302 * @param string|bool $placement the place/page the calendar is set to appear - passed on the the controls function
1303 * @param int|bool $courseid id of the course the calendar is displayed on - passed on the the controls function
1304 * @param int $time the unixtimestamp representing the date we want to view, this is used instead of $calmonth
1305 * and $calyear to support multiple calendars
1306 * @return string $content return html table for mini calendar
1307 */
1308function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyear = false, $placement = false,
1309 $courseid = false, $time = 0) {
1310 return \core_calendar\api::get_mini_calendar($courses, $groups, $users, $calmonth, $calyear, $placement,
1311 $courseid, $time);
1312}
1313
1314/**
1315 * Gets the calendar popup.
1316 *
1317 * It called at multiple points in from calendar_get_mini.
1318 * Copied and modified from calendar_get_mini.
1319 *
1320 * @param bool $today false except when called on the current day.
1321 * @param mixed $timestart $events[$eventid]->timestart, OR false if there are no events.
1322 * @param string $popupcontent content for the popup window/layout.
1323 * @return string eventid for the calendar_tooltip popup window/layout.
1324 */
1325function calendar_get_popup($today = false, $timestart, $popupcontent = '') {
1326 return \core_calendar\api::get_popup($today, $timestart, $popupcontent);
1327}
1328
1329/**
1330 * Gets the calendar upcoming event.
1331 *
1332 * @param array $courses array of courses
1333 * @param array|int|bool $groups array of groups, group id or boolean for all/no group events
1334 * @param array|int|bool $users array of users, user id or boolean for all/no user events
1335 * @param int $daysinfuture number of days in the future we 'll look
1336 * @param int $maxevents maximum number of events
1337 * @param int $fromtime start time
1338 * @return array $output array of upcoming events
1339 */
1340function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) {
1341 return \core_calendar\api::get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime);
1342}
1343
1344/**
1345 * Get a HTML link to a course.
1346 *
1347 * @param int $courseid the course id
1348 * @return string a link to the course (as HTML); empty if the course id is invalid
1349 */
1350function calendar_get_courselink($courseid) {
1351 return \core_calendar\api::get_courselink($courseid);
1352}
1353
1354/**
1355 * Get current module cache.
1356 *
1357 * @param array $coursecache list of course cache
1358 * @param string $modulename name of the module
1359 * @param int $instance module instance number
1360 * @return stdClass|bool $module information
1361 */
1362function calendar_get_module_cached(&$coursecache, $modulename, $instance) {
1363 // We have a new implementation of this function in the calendar API class,
1364 // so the old implementation must remain here.
1365 $module = get_coursemodule_from_instance($modulename, $instance);
1366
1367 if ($module === false) {
1368 return false;
1369 }
1370 if (!calendar_get_course_cached($coursecache, $module->course)) {
1371 return false;
1372 }
1373 return $module;
1374}
1375
1376/**
1377 * Get current course cache.
1378 *
1379 * @param array $coursecache list of course cache
1380 * @param int $courseid id of the course
1381 * @return stdClass $coursecache[$courseid] return the specific course cache
1382 */
1383function calendar_get_course_cached(&$coursecache, $courseid) {
1384 return \core_calendar\api::get_course_cached($coursecache, $courseid);
1385}
1386
1387/**
1388 * Get group from groupid for calendar display
1389 *
1390 * @param int $groupid
1391 * @return stdClass group object with fields 'id', 'name' and 'courseid'
1392 */
1393function calendar_get_group_cached($groupid) {
1394 return \core_calendar\api::get_group_cached($groupid);
1395}
1396
1397/**
1398 * Add calendar event metadata
1399 *
1400 * @param stdClass $event event info
1401 * @return stdClass $event metadata
1402 */
1403function calendar_add_event_metadata($event) {
1404 return \core_calendar\api::add_event_metadata($event);
1405}
1406
1407/**
1408 * Get calendar events by id.
1409 *
1410 * @since Moodle 2.5
1411 * @param array $eventids list of event ids
1412 * @return array Array of event entries, empty array if nothing found
1413 */
1414function calendar_get_events_by_id($eventids) {
1415 return \core_calendar\api::get_events_by_id($eventids);
1416}
1417
1418/**
1419 * Get control options for calendar.
1420 *
1421 * @param string $type of calendar
1422 * @param array $data calendar information
1423 * @return string $content return available control for the calender in html
1424 */
1425function calendar_top_controls($type, $data) {
1426 return \core_calendar\api::get_top_controls($type, $data);
1427}
1428
1429/**
1430 * Formats a filter control element.
1431 *
1432 * @param moodle_url $url of the filter
1433 * @param int $type constant defining the type filter
1434 * @return string html content of the element
1435 */
1436function calendar_filter_controls_element(moodle_url $url, $type) {
1437 return \core_calendar\api::get_filter_controls_element($url, $type);
1438}
1439
1440/**
1441 * Get the controls filter for calendar.
1442 *
1443 * Filter is used to hide calendar info from the display page.
1444 *
1445
1446 * @param moodle_url $returnurl return-url for filter controls
1447 * @return string $content return filter controls in html
1448 */
1449function calendar_filter_controls(moodle_url $returnurl) {
1450 return \core_calendar\api::get_filter_controls($returnurl);
1451}
1452
1453/**
1454 * Return the representation day.
1455 *
1456 * @param int $tstamp Timestamp in GMT
1457 * @param int|bool $now current Unix timestamp
1458 * @param bool $usecommonwords
1459 * @return string the formatted date/time
1460 */
1461function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) {
1462 return \core_calendar\api::get_day_representation($tstamp, $now, $usecommonwords);
1463}
1464
1465/**
1466 * return the formatted representation time.
1467 *
1468
1469 * @param int $time the timestamp in UTC, as obtained from the database
1470 * @return string the formatted date/time
1471 */
1472function calendar_time_representation($time) {
1473 return \core_calendar\api::get_time_representation($time);
1474}
1475
1476/**
1477 * Adds day, month, year arguments to a URL and returns a moodle_url object.
1478 *
1479 * @param string|moodle_url $linkbase
1480 * @param int $d The number of the day.
1481 * @param int $m The number of the month.
1482 * @param int $y The number of the year.
1483 * @param int $time the unixtime, used for multiple calendar support. The values $d,
1484 * $m and $y are kept for backwards compatibility.
1485 * @return moodle_url|null $linkbase
1486 */
1487function calendar_get_link_href($linkbase, $d, $m, $y, $time = 0) {
1488 return \core_calendar\api::get_link_href($linkbase, $d, $m, $y, $time);
1489}
1490
1491/**
1492 * Build and return a previous month HTML link, with an arrow.
1493 *
1494 * @param string $text The text label.
1495 * @param string|moodle_url $linkbase The URL stub.
1496 * @param int $d The number of the date.
1497 * @param int $m The number of the month.
1498 * @param int $y year The number of the year.
1499 * @param bool $accesshide Default visible, or hide from all except screenreaders.
1500 * @param int $time the unixtime, used for multiple calendar support. The values $d,
1501 * $m and $y are kept for backwards compatibility.
1502 * @return string HTML string.
1503 */
1504function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) {
1505 return \core_calendar\api::get_link_previous($text, $linkbase, $d, $m, $y, $accesshide, $time);
1506}
1507
1508/**
1509 * Build and return a next month HTML link, with an arrow.
1510 *
1511 * @param string $text The text label.
1512 * @param string|moodle_url $linkbase The URL stub.
1513 * @param int $d the number of the Day
1514 * @param int $m The number of the month.
1515 * @param int $y The number of the year.
1516 * @param bool $accesshide Default visible, or hide from all except screenreaders.
1517 * @param int $time the unixtime, used for multiple calendar support. The values $d,
1518 * $m and $y are kept for backwards compatibility.
1519 * @return string HTML string.
1520 */
1521function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) {
1522 return \core_calendar\api::get_link_next($text, $linkbase, $d, $m, $y, $accesshide, $time);
1523}
1524
1525/**
1526 * Return the number of days in month.
1527 *
1528 * @param int $month the number of the month.
1529 * @param int $year the number of the year
1530 * @return int
1531 */
1532function calendar_days_in_month($month, $year) {
1533 return \core_calendar\api::get_days_in_month($month, $year);
1534}
1535
1536/**
1537 * Get the next following month.
1538 *
1539 * @param int $month the number of the month.
1540 * @param int $year the number of the year.
1541 * @return array the following month
1542 */
1543function calendar_add_month($month, $year) {
1544 return \core_calendar\api::get_next_month($month, $year);
1545}
1546
1547/**
1548 * Get the previous month.
1549 *
1550 * @param int $month the number of the month.
1551 * @param int $year the number of the year.
1552 * @return array previous month
1553 */
1554function calendar_sub_month($month, $year) {
1555 return \core_calendar\api::get_prev_month($month, $year);
1556}
1557
1558/**
1559 * Get per-day basis events
1560 *
1561 * @param array $events list of events
1562 * @param int $month the number of the month
1563 * @param int $year the number of the year
1564 * @param array $eventsbyday event on specific day
1565 * @param array $durationbyday duration of the event in days
1566 * @param array $typesbyday event type (eg: global, course, user, or group)
1567 * @param array $courses list of courses
1568 * @return void
1569 */
1570function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) {
1571 \core_calendar\api::get_events_by_day($events, $month, $year, $eventsbyday, $durationbyday, $typesbyday, $courses);
1572}
1573
1574/**
1575 * Returns the courses to load events for.
1576 *
1577 * @param array $courseeventsfrom An array of courses to load calendar events for
1578 * @param bool $ignorefilters specify the use of filters, false is set as default
1579 * @return array An array of courses, groups, and user to load calendar events for based upon filters
1580 */
1581function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
1582 return \core_calendar\api::set_filters($courseeventsfrom, $ignorefilters);
1583}
1584
1585/**
1586 * Return the capability for editing calendar event.
1587 *
1588 * @param calendar_event $event event object
1589 * @return bool capability to edit event
1590 */
1591function calendar_edit_event_allowed($event) {
1592 return \core_calendar\api::can_edit_event($event);
1593}
1594
1595/**
1596 * Returns the default courses to display on the calendar when there isn't a specific
1597 * course to display.
1598 *
1599 * @return array $courses Array of courses to display
1600 */
1601function calendar_get_default_courses() {
1602 return \core_calendar\api::get_default_courses();
1603}
1604
1605/**
1606 * Get event format time.
1607 *
1608 * @param calendar_event $event event object
1609 * @param int $now current time in gmt
1610 * @param array $linkparams list of params for event link
1611 * @param bool $usecommonwords the words as formatted date/time.
1612 * @param int $showtime determine the show time GMT timestamp
1613 * @return string $eventtime link/string for event time
1614 */
1615function calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime = 0) {
1616 return \core_calendar\api::get_format_event_time($event, $now, $linkparams, $usecommonwords, $showtime);
1617}
1618
1619/**
1620 * Checks to see if the requested type of event should be shown for the given user.
1621 *
1622 * @param int $type The type to check the display for (default is to display all)
1623 * @param stdClass|int|null $user The user to check for - by default the current user
1624 * @return bool True if the tyep should be displayed false otherwise
1625 */
1626function calendar_show_event_type($type, $user = null) {
1627 return \core_calendar\api::show_event_type($type, $user);
1628}
1629
1630/**
1631 * Sets the display of the event type given $display.
1632 *
1633 * If $display = true the event type will be shown.
1634 * If $display = false the event type will NOT be shown.
1635 * If $display = null the current value will be toggled and saved.
1636 *
1637 * @param int $type object of CALENDAR_EVENT_XXX
1638 * @param bool $display option to display event type
1639 * @param stdClass|int $user moodle user object or id, null means current user
1640 */
1641function calendar_set_event_type_display($type, $display = null, $user = null) {
1642 \core_calendar\api::set_event_type_display($type, $display, $user);
1643}
1644
1645/**
1646 * Get calendar's allowed types.
1647 *
1648 * @param stdClass $allowed list of allowed edit for event type
1649 * @param stdClass|int $course object of a course or course id
1650 */
1651function calendar_get_allowed_types(&$allowed, $course = null) {
1652 \core_calendar\api::get_allowed_types($allowed, $course);
1653}
1654
1655/**
1656 * See if user can add calendar entries at all used to print the "New Event" button.
1657 *
1658 * @param stdClass $course object of a course or course id
1659 * @return bool has the capability to add at least one event type
1660 */
1661function calendar_user_can_add_event($course) {
1662 return \core_calendar\api::can_add_event_to_course($course);
1663}
1664
1665/**
1666 * Check wether the current user is permitted to add events.
1667 *
1668 * @param stdClass $event object of event
1669 * @return bool has the capability to add event
1670 */
1671function calendar_add_event_allowed($event) {
1672 return \core_calendar\api::can_add_event($event);
1673}
1674
1675/**
1676 * Returns option list for the poll interval setting.
1677 *
1678 * @return array An array of poll interval options. Interval => description.
1679 */
1680function calendar_get_pollinterval_choices() {
1681 return \core_calendar\api::get_poll_interval_choices();
1682}
1683
1684/**
1685 * Returns option list of available options for the calendar event type, given the current user and course.
1686 *
1687 * @param int $courseid The id of the course
1688 * @return array An array containing the event types the user can create.
1689 */
1690function calendar_get_eventtype_choices($courseid) {
1691 return \core_calendar\api::get_event_type_choices($courseid);
1692}
1693
1694/**
1695 * Add an iCalendar subscription to the database.
1696 *
1697 * @param stdClass $sub The subscription object (e.g. from the form)
1698 * @return int The insert ID, if any.
1699 */
1700function calendar_add_subscription($sub) {
1701 return \core_calendar\api::add_subscription($sub);
1702}
1703
1704/**
1705 * Add an iCalendar event to the Moodle calendar.
1706 *
1707 * @param stdClass $event The RFC-2445 iCalendar event
1708 * @param int $courseid The course ID
1709 * @param int $subscriptionid The iCalendar subscription ID
1710 * @param string $timezone The X-WR-TIMEZONE iCalendar property if provided
1711 * @throws dml_exception A DML specific exception is thrown for invalid subscriptionids.
1712 * @return int Code: CALENDAR_IMPORT_EVENT_UPDATED = updated, CALENDAR_IMPORT_EVENT_INSERTED = inserted, 0 = error
1713 */
1714function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone='UTC') {
1715 return \core_calendar\api::add_icalendar_event($event, $courseid, $subscriptionid, $timezone);
1716}
1717
1718/**
1719 * Update a subscription from the form data in one of the rows in the existing subscriptions table.
1720 *
1721 * @param int $subscriptionid The ID of the subscription we are acting upon.
1722 * @param int $pollinterval The poll interval to use.
1723 * @param int $action The action to be performed. One of update or remove.
1724 * @throws dml_exception if invalid subscriptionid is provided
1725 * @return string A log of the import progress, including errors
1726 */
1727function calendar_process_subscription_row($subscriptionid, $pollinterval, $action) {
1728 return \core_calendar\api::process_subscription_row($subscriptionid, $pollinterval, $action);
1729}
1730
1731/**
1732 * Delete subscription and all related events.
1733 *
1734 * @param int|stdClass $subscription subscription or it's id, which needs to be deleted.
1735 */
1736function calendar_delete_subscription($subscription) {
1737 \core_calendar\api::delete_subscription($subscription);
1738}
1739
1740/**
1741 * From a URL, fetch the calendar and return an iCalendar object.
1742 *
1743 * @param string $url The iCalendar URL
1744 * @return iCalendar The iCalendar object
1745 */
1746function calendar_get_icalendar($url) {
1747 return \core_calendar\api::get_icalendar($url);
1748}
1749
1750/**
1751 * Import events from an iCalendar object into a course calendar.
1752 *
1753 * @param iCalendar $ical The iCalendar object.
1754 * @param int $courseid The course ID for the calendar.
1755 * @param int $subscriptionid The subscription ID.
1756 * @return string A log of the import progress, including errors.
1757 */
1758function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) {
1759 return \core_calendar\api::import_icalendar_events($ical, $courseid, $subscriptionid);
1760}
1761
1762/**
1763 * Fetch a calendar subscription and update the events in the calendar.
1764 *
1765 * @param int $subscriptionid The course ID for the calendar.
1766 * @return string A log of the import progress, including errors.
1767 */
1768function calendar_update_subscription_events($subscriptionid) {
1769 return \core_calendar\api::update_subscription_events($subscriptionid);
1770}
1771
1772/**
1773 * Update a calendar subscription. Also updates the associated cache.
1774 *
1775 * @param stdClass|array $subscription Subscription record.
1776 * @throws coding_exception If something goes wrong
1777 * @since Moodle 2.5
1778 */
1779function calendar_update_subscription($subscription) {
1780 \core_calendar\api::update_subscription($subscription);
1781}
1782
1783/**
1784 * Checks to see if the user can edit a given subscription feed.
1785 *
1786 * @param mixed $subscriptionorid Subscription object or id
1787 * @return bool true if current user can edit the subscription else false
1788 */
1789function calendar_can_edit_subscription($subscriptionorid) {
1790 return \core_calendar\api::can_edit_subscription($subscriptionorid);
1791}
1792
1793/**
1794 * Helper function to determine the context of a calendar subscription.
1795 * Subscriptions can be created in two contexts COURSE, or USER.
1796 *
1797 * @param stdClass $subscription
1798 * @return context instance
1799 */
1800function calendar_get_calendar_context($subscription) {
1801 return \core_calendar\api::get_calendar_context($subscription);
1802}
1803
6e65554e
MG
1804/**
1805 * Implements callback user_preferences, whitelists preferences that users are allowed to update directly
1806 *
1807 * Used in {@see core_user::fill_preferences_cache()}, see also {@see useredit_update_user_preference()}
1808 *
1809 * @return array
1810 */
1811function core_calendar_user_preferences() {
1812 $preferences = [];
1813 $preferences['calendar_timeformat'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED, 'default' => '0',
1814 'choices' => array('0', CALENDAR_TF_12, CALENDAR_TF_24)
1815 );
1816 $preferences['calendar_startwday'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 0,
1817 'choices' => array(0, 1, 2, 3, 4, 5, 6));
1818 $preferences['calendar_maxevents'] = array('type' => PARAM_INT, 'choices' => range(1, 20));
1819 $preferences['calendar_lookahead'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 365,
1820 'choices' => array(365, 270, 180, 150, 120, 90, 60, 30, 21, 14, 7, 6, 5, 4, 3, 2, 1));
1821 $preferences['calendar_persistflt'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 0,
1822 'choices' => array(0, 1));
1823 return $preferences;
663640f5 1824}