Merge branch 'MDL-60667_master' of git://github.com/dmonllao/moodle
[moodle.git] / calendar / externallib.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/>.
18 /**
19  * External calendar API
20  *
21  * @package    core_calendar
22  * @category   external
23  * @copyright  2012 Ankit Agarwal
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  * @since Moodle 2.5
26  */
28 defined('MOODLE_INTERNAL') || die;
30 require_once("$CFG->libdir/externallib.php");
32 use \core_calendar\local\api as local_api;
33 use \core_calendar\local\event\container as event_container;
34 use \core_calendar\local\event\forms\create as create_event_form;
35 use \core_calendar\local\event\forms\update as update_event_form;
36 use \core_calendar\local\event\mappers\create_update_form_mapper;
37 use \core_calendar\external\event_exporter;
38 use \core_calendar\external\events_exporter;
39 use \core_calendar\external\events_grouped_by_course_exporter;
40 use \core_calendar\external\events_related_objects_cache;
42 /**
43  * Calendar external functions
44  *
45  * @package    core_calendar
46  * @category   external
47  * @copyright  2012 Ankit Agarwal
48  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
49  * @since Moodle 2.5
50  */
51 class core_calendar_external extends external_api {
54     /**
55      * Returns description of method parameters
56      *
57      * @return external_function_parameters
58      * @since Moodle 2.5
59      */
60     public static function delete_calendar_events_parameters() {
61         return new external_function_parameters(
62                 array('events' => new external_multiple_structure(
63                         new external_single_structure(
64                                 array(
65                                         'eventid' => new external_value(PARAM_INT, 'Event ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
66                                         'repeat'  => new external_value(PARAM_BOOL, 'Delete comeplete series if repeated event')
67                                 ), 'List of events to delete'
68                         )
69                     )
70                 )
71         );
72     }
74     /**
75      * Delete Calendar events
76      *
77      * @param array $eventids A list of event ids with repeat flag to delete
78      * @return null
79      * @since Moodle 2.5
80      */
81     public static function delete_calendar_events($events) {
82         global $CFG, $DB;
83         require_once($CFG->dirroot."/calendar/lib.php");
85         // Parameter validation.
86         $params = self::validate_parameters(self:: delete_calendar_events_parameters(), array('events' => $events));
88         $transaction = $DB->start_delegated_transaction();
90         foreach ($params['events'] as $event) {
91             $eventobj = calendar_event::load($event['eventid']);
93             // Let's check if the user is allowed to delete an event.
94             if (!calendar_delete_event_allowed($eventobj)) {
95                 throw new moodle_exception('nopermissions', 'error', '', get_string('deleteevent', 'calendar'));
96             }
97             // Time to do the magic.
98             $eventobj->delete($event['repeat']);
99         }
101         // Everything done smoothly, let's commit.
102         $transaction->allow_commit();
104         return null;
105     }
107     /**
108      * Returns description of method result value
109      *
110      * @return external_description
111      * @since Moodle 2.5
112      */
113     public static function  delete_calendar_events_returns() {
114         return null;
115     }
117     /**
118      * Returns description of method parameters
119      *
120      * @return external_function_parameters
121      * @since Moodle 2.5
122      */
123     public static function get_calendar_events_parameters() {
124         return new external_function_parameters(
125                 array('events' => new external_single_structure(
126                             array(
127                                     'eventids' => new external_multiple_structure(
128                                             new external_value(PARAM_INT, 'event ids')
129                                             , 'List of event ids',
130                                             VALUE_DEFAULT, array(), NULL_ALLOWED
131                                                 ),
132                                     'courseids' => new external_multiple_structure(
133                                             new external_value(PARAM_INT, 'course ids')
134                                             , 'List of course ids for which events will be returned',
135                                             VALUE_DEFAULT, array(), NULL_ALLOWED
136                                                 ),
137                                     'groupids' => new external_multiple_structure(
138                                             new external_value(PARAM_INT, 'group ids')
139                                             , 'List of group ids for which events should be returned',
140                                             VALUE_DEFAULT, array(), NULL_ALLOWED
141                                                 )
142                             ), 'Event details', VALUE_DEFAULT, array()),
143                     'options' => new external_single_structure(
144                             array(
145                                     'userevents' => new external_value(PARAM_BOOL,
146                                              "Set to true to return current user's user events",
147                                              VALUE_DEFAULT, true, NULL_ALLOWED),
148                                     'siteevents' => new external_value(PARAM_BOOL,
149                                              "Set to true to return global events",
150                                              VALUE_DEFAULT, true, NULL_ALLOWED),
151                                     'timestart' => new external_value(PARAM_INT,
152                                              "Time from which events should be returned",
153                                              VALUE_DEFAULT, 0, NULL_ALLOWED),
154                                     'timeend' => new external_value(PARAM_INT,
155                                              "Time to which the events should be returned. We treat 0 and null as no end",
156                                              VALUE_DEFAULT, 0, NULL_ALLOWED),
157                                     'ignorehidden' => new external_value(PARAM_BOOL,
158                                              "Ignore hidden events or not",
159                                              VALUE_DEFAULT, true, NULL_ALLOWED),
161                             ), 'Options', VALUE_DEFAULT, array())
162                 )
163         );
164     }
166     /**
167      * Get Calendar events
168      *
169      * @param array $events A list of events
170      * @param array $options various options
171      * @return array Array of event details
172      * @since Moodle 2.5
173      */
174     public static function get_calendar_events($events = array(), $options = array()) {
175         global $SITE, $DB, $USER, $CFG;
176         require_once($CFG->dirroot."/calendar/lib.php");
178         // Parameter validation.
179         $params = self::validate_parameters(self::get_calendar_events_parameters(), array('events' => $events, 'options' => $options));
180         $funcparam = array('courses' => array(), 'groups' => array());
181         $hassystemcap = has_capability('moodle/calendar:manageentries', context_system::instance());
182         $warnings = array();
184         // Let us findout courses that we can return events from.
185         if (!$hassystemcap) {
186             $courses = enrol_get_my_courses('id');
187             $courses = array_keys($courses);
188             foreach ($params['events']['courseids'] as $id) {
189                try {
190                     $context = context_course::instance($id);
191                     self::validate_context($context);
192                     $funcparam['courses'][] = $id;
193                 } catch (Exception $e) {
194                     $warnings[] = array(
195                         'item' => 'course',
196                         'itemid' => $id,
197                         'warningcode' => 'nopermissions',
198                         'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
199                     );
200                 }
201             }
202         } else {
203             $courses = $params['events']['courseids'];
204             $funcparam['courses'] = $courses;
205         }
206         // Now get categories we can get events from.
207         $categories = \coursecat::get_all();
209         // Let us findout groups that we can return events from.
210         if (!$hassystemcap) {
211             $groups = groups_get_my_groups();
212             $groups = array_keys($groups);
213             foreach ($params['events']['groupids'] as $id) {
214                 if (in_array($id, $groups)) {
215                     $funcparam['groups'][] = $id;
216                 } else {
217                     $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this group');
218                 }
219             }
220         } else {
221             $groups = $params['events']['groupids'];
222             $funcparam['groups'] = $groups;
223         }
225         // Do we need user events?
226         if (!empty($params['options']['userevents'])) {
227             $funcparam['users'] = array($USER->id);
228         } else {
229             $funcparam['users'] = false;
230         }
232         // Do we need site events?
233         if (!empty($params['options']['siteevents'])) {
234             $funcparam['courses'][] = $SITE->id;
235         }
237         // We treat 0 and null as no end.
238         if (empty($params['options']['timeend'])) {
239             $params['options']['timeend'] = PHP_INT_MAX;
240         }
242         // Event list does not check visibility and permissions, we'll check that later.
243         $eventlist = calendar_get_legacy_events($params['options']['timestart'], $params['options']['timeend'],
244             $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, $params['options']['ignorehidden']);
246         // WS expects arrays.
247         $events = array();
249         // We need to get events asked for eventids.
250         if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) {
251             $eventlist += $eventsbyid;
252         }
254         foreach ($eventlist as $eventid => $eventobj) {
255             $event = (array) $eventobj;
256             // Description formatting.
257             $calendareventobj = new calendar_event($event);
258             list($event['description'], $event['format']) = $calendareventobj->format_external_text();
260             if ($hassystemcap) {
261                 // User can see everything, no further check is needed.
262                 $events[$eventid] = $event;
263             } else if (!empty($eventobj->modulename)) {
264                 $courseid = $eventobj->courseid;
265                 if (!$courseid) {
266                     if (!$calendareventobj->context || !($context = $calendareventobj->context->get_course_context(false))) {
267                         continue;
268                     }
269                     $courseid = $context->instanceid;
270                 }
271                 $instances = get_fast_modinfo($courseid)->get_instances_of($eventobj->modulename);
272                 if (!empty($instances[$eventobj->instance]->uservisible)) {
273                     $events[$eventid] = $event;
274                 }
275             } else {
276                 // Can the user actually see this event?
277                 $eventobj = calendar_event::load($eventobj);
278                 if ((($eventobj->courseid == $SITE->id) && (empty($eventobj->categoryid))) ||
279                             (!empty($eventobj->categoryid) && in_array($eventobj->categoryid, $categories)) ||
280                             (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) ||
281                             (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) ||
282                             ($USER->id == $eventobj->userid) ||
283                             (calendar_edit_event_allowed($eventobj))) {
284                     $events[$eventid] = $event;
285                 } else {
286                     $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event');
287                 }
288             }
289         }
290         return array('events' => $events, 'warnings' => $warnings);
291     }
293     /**
294      * Returns description of method result value
295      *
296      * @return external_description
297      * @since Moodle 2.5
298      */
299     public static function  get_calendar_events_returns() {
300         return new external_single_structure(array(
301                 'events' => new external_multiple_structure( new external_single_structure(
302                         array(
303                             'id' => new external_value(PARAM_INT, 'event id'),
304                             'name' => new external_value(PARAM_TEXT, 'event name'),
305                             'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL, null, NULL_ALLOWED),
306                             'format' => new external_format_value('description'),
307                             'courseid' => new external_value(PARAM_INT, 'course id'),
308                             'groupid' => new external_value(PARAM_INT, 'group id'),
309                             'userid' => new external_value(PARAM_INT, 'user id'),
310                             'repeatid' => new external_value(PARAM_INT, 'repeat id'),
311                             'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL, null, NULL_ALLOWED),
312                             'instance' => new external_value(PARAM_INT, 'instance id'),
313                             'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
314                             'timestart' => new external_value(PARAM_INT, 'timestart'),
315                             'timeduration' => new external_value(PARAM_INT, 'time duration'),
316                             'visible' => new external_value(PARAM_INT, 'visible'),
317                             'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, null, NULL_NOT_ALLOWED),
318                             'sequence' => new external_value(PARAM_INT, 'sequence'),
319                             'timemodified' => new external_value(PARAM_INT, 'time modified'),
320                             'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL, null, NULL_ALLOWED),
321                         ), 'event')
322                  ),
323                  'warnings' => new external_warnings()
324                 )
325         );
326     }
328     /**
329      * Returns description of method parameters.
330      *
331      * @since Moodle 3.3
332      * @return external_function_parameters
333      */
334     public static function get_calendar_action_events_by_timesort_parameters() {
335         return new external_function_parameters(
336             array(
337                 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, 0),
338                 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
339                 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
340                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20)
341             )
342         );
343     }
345     /**
346      * Get calendar action events based on the timesort value.
347      *
348      * @since Moodle 3.3
349      * @param null|int $timesortfrom Events after this time (inclusive)
350      * @param null|int $timesortto Events before this time (inclusive)
351      * @param null|int $aftereventid Get events with ids greater than this one
352      * @param int $limitnum Limit the number of results to this value
353      * @return array
354      */
355     public static function get_calendar_action_events_by_timesort($timesortfrom = 0, $timesortto = null,
356                                                        $aftereventid = 0, $limitnum = 20) {
357         global $CFG, $PAGE, $USER;
359         require_once($CFG->dirroot . '/calendar/lib.php');
361         $user = null;
362         $params = self::validate_parameters(
363             self::get_calendar_action_events_by_timesort_parameters(),
364             [
365                 'timesortfrom' => $timesortfrom,
366                 'timesortto' => $timesortto,
367                 'aftereventid' => $aftereventid,
368                 'limitnum' => $limitnum,
369             ]
370         );
371         $context = \context_user::instance($USER->id);
372         self::validate_context($context);
374         if (empty($params['aftereventid'])) {
375             $params['aftereventid'] = null;
376         }
378         $renderer = $PAGE->get_renderer('core_calendar');
379         $events = local_api::get_action_events_by_timesort(
380             $params['timesortfrom'],
381             $params['timesortto'],
382             $params['aftereventid'],
383             $params['limitnum']
384         );
386         $exportercache = new events_related_objects_cache($events);
387         $exporter = new events_exporter($events, ['cache' => $exportercache]);
389         return $exporter->export($renderer);
390     }
392     /**
393      * Returns description of method result value.
394      *
395      * @since Moodle 3.3
396      * @return external_description
397      */
398     public static function get_calendar_action_events_by_timesort_returns() {
399         return events_exporter::get_read_structure();
400     }
402     /**
403      * Returns description of method parameters.
404      *
405      * @return external_function_parameters
406      */
407     public static function get_calendar_action_events_by_course_parameters() {
408         return new external_function_parameters(
409             array(
410                 'courseid' => new external_value(PARAM_INT, 'Course id'),
411                 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null),
412                 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
413                 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
414                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20)
415             )
416         );
417     }
419     /**
420      * Get calendar action events for the given course.
421      *
422      * @since Moodle 3.3
423      * @param int $courseid Only events in this course
424      * @param null|int $timesortfrom Events after this time (inclusive)
425      * @param null|int $timesortto Events before this time (inclusive)
426      * @param null|int $aftereventid Get events with ids greater than this one
427      * @param int $limitnum Limit the number of results to this value
428      * @return array
429      */
430     public static function get_calendar_action_events_by_course(
431         $courseid, $timesortfrom = null, $timesortto = null, $aftereventid = 0, $limitnum = 20) {
433         global $CFG, $PAGE, $USER;
435         require_once($CFG->dirroot . '/calendar/lib.php');
437         $user = null;
438         $params = self::validate_parameters(
439             self::get_calendar_action_events_by_course_parameters(),
440             [
441                 'courseid' => $courseid,
442                 'timesortfrom' => $timesortfrom,
443                 'timesortto' => $timesortto,
444                 'aftereventid' => $aftereventid,
445                 'limitnum' => $limitnum,
446             ]
447         );
448         $context = \context_user::instance($USER->id);
449         self::validate_context($context);
451         if (empty($params['aftereventid'])) {
452             $params['aftereventid'] = null;
453         }
455         $courses = enrol_get_my_courses('*', 'visible DESC,sortorder ASC', 0, [$courseid]);
456         $courses = array_values($courses);
458         if (empty($courses)) {
459             return [];
460         }
462         $course = $courses[0];
463         $renderer = $PAGE->get_renderer('core_calendar');
464         $events = local_api::get_action_events_by_course(
465             $course,
466             $params['timesortfrom'],
467             $params['timesortto'],
468             $params['aftereventid'],
469             $params['limitnum']
470         );
472         $exportercache = new events_related_objects_cache($events, $courses);
473         $exporter = new events_exporter($events, ['cache' => $exportercache]);
475         return $exporter->export($renderer);
476     }
478     /**
479      * Returns description of method result value.
480      *
481      * @return external_description
482      */
483     public static function get_calendar_action_events_by_course_returns() {
484         return events_exporter::get_read_structure();
485     }
487     /**
488      * Returns description of method parameters.
489      *
490      * @return external_function_parameters
491      */
492     public static function get_calendar_action_events_by_courses_parameters() {
493         return new external_function_parameters(
494             array(
495                 'courseids' => new external_multiple_structure(
496                     new external_value(PARAM_INT, 'Course id')
497                 ),
498                 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null),
499                 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
500                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 10)
501             )
502         );
503     }
505     /**
506      * Get calendar action events for a given list of courses.
507      *
508      * @since Moodle 3.3
509      * @param array $courseids Only include events for these courses
510      * @param null|int $timesortfrom Events after this time (inclusive)
511      * @param null|int $timesortto Events before this time (inclusive)
512      * @param int $limitnum Limit the number of results per course to this value
513      * @return array
514      */
515     public static function get_calendar_action_events_by_courses(
516         array $courseids, $timesortfrom = null, $timesortto = null, $limitnum = 10) {
518         global $CFG, $PAGE, $USER;
520         require_once($CFG->dirroot . '/calendar/lib.php');
522         $user = null;
523         $params = self::validate_parameters(
524             self::get_calendar_action_events_by_courses_parameters(),
525             [
526                 'courseids' => $courseids,
527                 'timesortfrom' => $timesortfrom,
528                 'timesortto' => $timesortto,
529                 'limitnum' => $limitnum,
530             ]
531         );
532         $context = \context_user::instance($USER->id);
533         self::validate_context($context);
535         if (empty($params['courseids'])) {
536             return ['groupedbycourse' => []];
537         }
539         $renderer = $PAGE->get_renderer('core_calendar');
540         $courses = enrol_get_my_courses('*', 'visible DESC,sortorder ASC', 0, $params['courseids']);
541         $courses = array_values($courses);
543         if (empty($courses)) {
544             return ['groupedbycourse' => []];
545         }
547         $events = local_api::get_action_events_by_courses(
548             $courses,
549             $params['timesortfrom'],
550             $params['timesortto'],
551             $params['limitnum']
552         );
554         if (empty($events)) {
555             return ['groupedbycourse' => []];
556         }
558         $exportercache = new events_related_objects_cache($events, $courses);
559         $exporter = new events_grouped_by_course_exporter($events, ['cache' => $exportercache]);
561         return $exporter->export($renderer);
562     }
564     /**
565      * Returns description of method result value.
566      *
567      * @return external_description
568      */
569     public static function get_calendar_action_events_by_courses_returns() {
570         return events_grouped_by_course_exporter::get_read_structure();
571     }
573     /**
574      * Returns description of method parameters.
575      *
576      * @return external_function_parameters.
577      * @since Moodle 2.5
578      */
579     public static function create_calendar_events_parameters() {
580         // Userid is always current user, so no need to get it from client.
581         // Module based calendar events are not allowed here. Hence no need of instance and modulename.
582         // subscription id and uuid is not allowed as this is not an ical api.
583         return new external_function_parameters(
584                 array('events' => new external_multiple_structure(
585                         new external_single_structure(
586                             array(
587                                 'name' => new external_value(PARAM_TEXT, 'event name', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
588                                 'description' => new external_value(PARAM_RAW, 'Description', VALUE_DEFAULT, null, NULL_ALLOWED),
589                                 'format' => new external_format_value('description', VALUE_DEFAULT),
590                                 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
591                                 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
592                                 'repeats' => new external_value(PARAM_INT, 'number of repeats', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
593                                 'eventtype' => new external_value(PARAM_TEXT, 'Event type', VALUE_DEFAULT, 'user', NULL_NOT_ALLOWED),
594                                 'timestart' => new external_value(PARAM_INT, 'timestart', VALUE_DEFAULT, time(), NULL_NOT_ALLOWED),
595                                 'timeduration' => new external_value(PARAM_INT, 'time duration', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
596                                 'visible' => new external_value(PARAM_INT, 'visible', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
597                                 'sequence' => new external_value(PARAM_INT, 'sequence', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
598                             ), 'event')
599                 )
600             )
601         );
602     }
604     /**
605      * Delete Calendar events.
606      *
607      * @param array $events A list of events to create.
608      * @return array array of events created.
609      * @since Moodle 2.5
610      * @throws moodle_exception if user doesnt have the permission to create events.
611      */
612     public static function create_calendar_events($events) {
613         global $CFG, $DB, $USER;
614         require_once($CFG->dirroot."/calendar/lib.php");
616         // Parameter validation.
617         $params = self::validate_parameters(self::create_calendar_events_parameters(), array('events' => $events));
619         $transaction = $DB->start_delegated_transaction();
620         $return = array();
621         $warnings = array();
623         foreach ($params['events'] as $event) {
625             // Let us set some defaults.
626             $event['userid'] = $USER->id;
627             $event['modulename'] = '';
628             $event['instance'] = 0;
629             $event['subscriptionid'] = null;
630             $event['uuid']= '';
631             $event['format'] = external_validate_format($event['format']);
632             if ($event['repeats'] > 0) {
633                 $event['repeat'] = 1;
634             } else {
635                 $event['repeat'] = 0;
636             }
638             $eventobj = new calendar_event($event);
640             // Let's check if the user is allowed to delete an event.
641             if (!calendar_add_event_allowed($eventobj)) {
642                 $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event');
643                 continue;
644             }
645             // Let's create the event.
646             $var = $eventobj->create($event);
647             $var = (array)$var->properties();
648             if ($event['repeat']) {
649                 $children = $DB->get_records('event', array('repeatid' => $var['id']));
650                 foreach ($children as $child) {
651                     $return[] = (array) $child;
652                 }
653             } else {
654                 $return[] = $var;
655             }
656         }
658         // Everything done smoothly, let's commit.
659         $transaction->allow_commit();
660         return array('events' => $return, 'warnings' => $warnings);
661     }
663     /**
664      * Returns description of method result value.
665      *
666      * @return external_description.
667      * @since Moodle 2.5
668      */
669     public static function  create_calendar_events_returns() {
670             return new external_single_structure(
671                     array(
672                         'events' => new external_multiple_structure( new external_single_structure(
673                                 array(
674                                     'id' => new external_value(PARAM_INT, 'event id'),
675                                     'name' => new external_value(PARAM_TEXT, 'event name'),
676                                     'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL),
677                                     'format' => new external_format_value('description'),
678                                     'courseid' => new external_value(PARAM_INT, 'course id'),
679                                     'groupid' => new external_value(PARAM_INT, 'group id'),
680                                     'userid' => new external_value(PARAM_INT, 'user id'),
681                                     'repeatid' => new external_value(PARAM_INT, 'repeat id', VALUE_OPTIONAL),
682                                     'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL),
683                                     'instance' => new external_value(PARAM_INT, 'instance id'),
684                                     'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
685                                     'timestart' => new external_value(PARAM_INT, 'timestart'),
686                                     'timeduration' => new external_value(PARAM_INT, 'time duration'),
687                                     'visible' => new external_value(PARAM_INT, 'visible'),
688                                     'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
689                                     'sequence' => new external_value(PARAM_INT, 'sequence'),
690                                     'timemodified' => new external_value(PARAM_INT, 'time modified'),
691                                     'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL),
692                                 ), 'event')
693                         ),
694                       'warnings' => new external_warnings()
695                     )
696             );
697     }
699     /**
700      * Returns description of method parameters.
701      *
702      * @return external_function_parameters
703      */
704     public static function get_calendar_event_by_id_parameters() {
705         return new external_function_parameters(
706             array(
707                 'eventid' => new external_value(PARAM_INT, 'The event id to be retrieved'),
708             )
709         );
710     }
712     /**
713      * Get calendar event by id.
714      *
715      * @param int $eventid The calendar event id to be retrieved.
716      * @return array Array of event details
717      */
718     public static function get_calendar_event_by_id($eventid) {
719         global $CFG, $PAGE, $USER;
720         require_once($CFG->dirroot."/calendar/lib.php");
722         $params = self::validate_parameters(self::get_calendar_event_by_id_parameters(), ['eventid' => $eventid]);
723         $context = \context_user::instance($USER->id);
725         self::validate_context($context);
726         $warnings = array();
728         $legacyevent = calendar_event::load($eventid);
729         // Must check we can see this event.
730         if (!calendar_view_event_allowed($legacyevent)) {
731             // We can't return a warning in this case because the event is not optional.
732             // We don't know the context for the event and it's not worth loading it.
733             $syscontext = context_system::instance();
734             throw new \required_capability_exception($syscontext, 'moodle/course:view', 'nopermission', '');
735         }
737         $legacyevent->count_repeats();
739         $eventmapper = event_container::get_event_mapper();
740         $event = $eventmapper->from_legacy_event_to_event($legacyevent);
742         $cache = new events_related_objects_cache([$event]);
743         $relatedobjects = [
744             'context' => $cache->get_context($event),
745             'course' => $cache->get_course($event),
746         ];
748         $exporter = new event_exporter($event, $relatedobjects);
749         $renderer = $PAGE->get_renderer('core_calendar');
751         return array('event' => $exporter->export($renderer), 'warnings' => $warnings);
752     }
754     /**
755      * Returns description of method result value
756      *
757      * @return external_description
758      */
759     public static function get_calendar_event_by_id_returns() {
760         $eventstructure = event_exporter::get_read_structure();
762         return new external_single_structure(array(
763             'event' => $eventstructure,
764             'warnings' => new external_warnings()
765             )
766         );
767     }
769     /**
770      * Returns description of method parameters.
771      *
772      * @return external_function_parameters.
773      */
774     public static function submit_create_update_form_parameters() {
775         return new external_function_parameters(
776             [
777                 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'),
778             ]
779         );
780     }
782     /**
783      * Handles the event form submission.
784      *
785      * @param string $formdata The event form data in a URI encoded param string
786      * @return array The created or modified event
787      * @throws moodle_exception
788      */
789     public static function submit_create_update_form($formdata) {
790         global $CFG, $USER, $PAGE;
791         require_once($CFG->dirroot."/calendar/lib.php");
792         require_once($CFG->libdir."/filelib.php");
794         // Parameter validation.
795         $params = self::validate_parameters(self::submit_create_update_form_parameters(), ['formdata' => $formdata]);
796         $context = \context_user::instance($USER->id);
797         $data = [];
799         self::validate_context($context);
800         parse_str($params['formdata'], $data);
802         if (!empty($data['id'])) {
803             $eventid = clean_param($data['id'], PARAM_INT);
804             $legacyevent = calendar_event::load($eventid);
805             $legacyevent->count_repeats();
806             $formoptions = ['event' => $legacyevent];
807             $mform = new update_event_form(null, $formoptions, 'post', '', null, true, $data);
808         } else {
809             $legacyevent = null;
810             $mform = new create_event_form(null, null, 'post', '', null, true, $data);
811         }
813         if ($validateddata = $mform->get_data()) {
814             $formmapper = new create_update_form_mapper();
815             $properties = $formmapper->from_data_to_event_properties($validateddata);
817             if (is_null($legacyevent)) {
818                 $legacyevent = new \calendar_event($properties);
819                 // Need to do this in order to initialise the description
820                 // property which then triggers the update function below
821                 // to set the appropriate default properties on the event.
822                 $properties = $legacyevent->properties(true);
823             }
825             if (!calendar_edit_event_allowed($legacyevent, true)) {
826                 print_error('nopermissiontoupdatecalendar');
827             }
829             $legacyevent->update($properties);
830             $eventcontext = $legacyevent->context;
832             file_remove_editor_orphaned_files($validateddata->description);
834             // Take any files added to the description draft file area and
835             // convert them into the proper event description file area. Also
836             // parse the description text and replace the URLs to the draft files
837             // with the @@PLUGIN_FILE@@ placeholder to be persisted in the DB.
838             $description = file_save_draft_area_files(
839                 $validateddata->description['itemid'],
840                 $eventcontext->id,
841                 'calendar',
842                 'event_description',
843                 $legacyevent->id,
844                 create_event_form::build_editor_options($eventcontext),
845                 $validateddata->description['text']
846             );
848             // If draft files were found then we need to save the new
849             // description value.
850             if ($description != $validateddata->description['text']) {
851                 $properties->id = $legacyevent->id;
852                 $properties->description = $description;
853                 $legacyevent->update($properties);
854             }
856             $eventmapper = event_container::get_event_mapper();
857             $event = $eventmapper->from_legacy_event_to_event($legacyevent);
858             $cache = new events_related_objects_cache([$event]);
859             $relatedobjects = [
860                 'context' => $cache->get_context($event),
861                 'course' => $cache->get_course($event),
862             ];
863             $exporter = new event_exporter($event, $relatedobjects);
864             $renderer = $PAGE->get_renderer('core_calendar');
866             return [ 'event' => $exporter->export($renderer) ];
867         } else {
868             return [ 'validationerror' => true ];
869         }
870     }
872     /**
873      * Returns description of method result value.
874      *
875      * @return external_description.
876      */
877     public static function  submit_create_update_form_returns() {
878         $eventstructure = event_exporter::get_read_structure();
879         $eventstructure->required = VALUE_OPTIONAL;
881         return new external_single_structure(
882             array(
883                 'event' => $eventstructure,
884                 'validationerror' => new external_value(PARAM_BOOL, 'Invalid form data', VALUE_DEFAULT, false),
885             )
886         );
887     }
889     /**
890      * Get data for the monthly calendar view.
891      *
892      * @param   int     $year The year to be shown
893      * @param   int     $month The month to be shown
894      * @param   int     $courseid The course to be included
895      * @param   int     $categoryid The category to be included
896      * @param   bool    $includenavigation Whether to include navigation
897      * @return  array
898      */
899     public static function get_calendar_monthly_view($year, $month, $courseid, $categoryid, $includenavigation) {
900         global $CFG, $DB, $USER, $PAGE;
901         require_once($CFG->dirroot."/calendar/lib.php");
903         // Parameter validation.
904         $params = self::validate_parameters(self::get_calendar_monthly_view_parameters(), [
905             'year' => $year,
906             'month' => $month,
907             'courseid' => $courseid,
908             'categoryid' => $categoryid,
909             'includenavigation' => $includenavigation,
910         ]);
912         $context = \context_user::instance($USER->id);
913         self::validate_context($context);
914         $PAGE->set_url('/calendar/');
916         $type = \core_calendar\type_factory::get_calendar_instance();
918         $time = $type->convert_to_timestamp($params['year'], $params['month'], 1);
919         $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']);
920         self::validate_context($calendar->context);
922         list($data, $template) = calendar_get_view($calendar, 'month', $params['includenavigation']);
924         return $data;
925     }
927     /**
928      * Returns description of method parameters.
929      *
930      * @return external_function_parameters
931      */
932     public static function get_calendar_monthly_view_parameters() {
933         return new external_function_parameters(
934             [
935                 'year' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED),
936                 'month' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED),
937                 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
938                 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
939                 'includenavigation' => new external_value(
940                     PARAM_BOOL,
941                     'Whether to show course navigation',
942                     VALUE_DEFAULT,
943                     true,
944                     NULL_ALLOWED
945                 ),
946             ]
947         );
948     }
950     /**
951      * Returns description of method result value.
952      *
953      * @return external_description
954      */
955     public static function get_calendar_monthly_view_returns() {
956         return \core_calendar\external\month_exporter::get_read_structure();
957     }
959     /**
960      * Get data for the daily calendar view.
961      *
962      * @param   int     $year The year to be shown
963      * @param   int     $month The month to be shown
964      * @param   int     $day The day to be shown
965      * @param   int     $courseid The course to be included
966      * @return  array
967      */
968     public static function get_calendar_day_view($year, $month, $day, $courseid, $categoryid) {
969         global $CFG, $DB, $USER, $PAGE;
970         require_once($CFG->dirroot."/calendar/lib.php");
972         // Parameter validation.
973         $params = self::validate_parameters(self::get_calendar_day_view_parameters(), [
974             'year' => $year,
975             'month' => $month,
976             'day' => $day,
977             'courseid' => $courseid,
978             'categoryid' => $categoryid,
979         ]);
981         $context = \context_user::instance($USER->id);
982         self::validate_context($context);
984         $type = \core_calendar\type_factory::get_calendar_instance();
986         $time = $type->convert_to_timestamp($params['year'], $params['month'], $params['day']);
987         $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']);
988         self::validate_context($calendar->context);
990         list($data, $template) = calendar_get_view($calendar, 'day');
992         return $data;
993     }
995     /**
996      * Returns description of method parameters.
997      *
998      * @return external_function_parameters
999      */
1000     public static function get_calendar_day_view_parameters() {
1001         return new external_function_parameters(
1002             [
1003                 'year' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED),
1004                 'month' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED),
1005                 'day' => new external_value(PARAM_INT, 'Day to be viewed', VALUE_REQUIRED),
1006                 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
1007                 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
1008             ]
1009         );
1010     }
1012     /**
1013      * Returns description of method result value.
1014      *
1015      * @return external_description
1016      */
1017     public static function get_calendar_day_view_returns() {
1018         return \core_calendar\external\calendar_day_exporter::get_read_structure();
1019     }
1022     /**
1023      * Returns description of method parameters.
1024      *
1025      * @return external_function_parameters
1026      */
1027     public static function update_event_start_day_parameters() {
1028         return new external_function_parameters(
1029             [
1030                 'eventid' => new external_value(PARAM_INT, 'Id of event to be updated', VALUE_REQUIRED),
1031                 'daytimestamp' => new external_value(PARAM_INT, 'Timestamp for the new start day', VALUE_REQUIRED),
1032             ]
1033         );
1034     }
1036     /**
1037      * Change the start day for the given calendar event to the day that
1038      * corresponds with the provided timestamp.
1039      *
1040      * The timestamp only needs to be anytime within the desired day as only
1041      * the date data is extracted from it.
1042      *
1043      * The event's original time of day is maintained, only the date is shifted.
1044      *
1045      * @param int $eventid Id of event to be updated
1046      * @param int $daytimestamp Timestamp for the new start day
1047      * @return  array
1048      */
1049     public static function update_event_start_day($eventid, $daytimestamp) {
1050         global $USER, $PAGE;
1052         // Parameter validation.
1053         $params = self::validate_parameters(self::update_event_start_day_parameters(), [
1054             'eventid' => $eventid,
1055             'daytimestamp' => $daytimestamp,
1056         ]);
1058         $vault = event_container::get_event_vault();
1059         $mapper = event_container::get_event_mapper();
1060         $event = $vault->get_event_by_id($eventid);
1062         if (!$event) {
1063             throw new \moodle_exception('Unable to find event with id ' . $eventid);
1064         }
1066         $legacyevent = $mapper->from_event_to_legacy_event($event);
1068         if (!calendar_edit_event_allowed($legacyevent, true)) {
1069             print_error('nopermissiontoupdatecalendar');
1070         }
1072         self::validate_context($legacyevent->context);
1074         $newdate = usergetdate($daytimestamp);
1075         $startdatestring = implode('-', [$newdate['year'], $newdate['mon'], $newdate['mday']]);
1076         $startdate = new DateTimeImmutable($startdatestring);
1077         $event = local_api::update_event_start_day($event, $startdate);
1078         $cache = new events_related_objects_cache([$event]);
1079         $relatedobjects = [
1080             'context' => $cache->get_context($event),
1081             'course' => $cache->get_course($event),
1082         ];
1083         $exporter = new event_exporter($event, $relatedobjects);
1084         $renderer = $PAGE->get_renderer('core_calendar');
1086         return array('event' => $exporter->export($renderer));
1087     }
1089     /**
1090      * Returns description of method result value.
1091      *
1092      * @return external_description
1093      */
1094     public static function update_event_start_day_returns() {
1095         return new external_single_structure(
1096             array(
1097                 'event' => event_exporter::get_read_structure()
1098             )
1099         );
1100     }
1102     /**
1103      * Get data for the monthly calendar view.
1104      *
1105      * @param   int     $courseid The course to be included
1106      * @param   int     $categoryid The category to be included
1107      * @return  array
1108      */
1109     public static function get_calendar_upcoming_view($courseid, $categoryid) {
1110         global $CFG, $DB, $USER, $PAGE;
1111         require_once($CFG->dirroot."/calendar/lib.php");
1113         // Parameter validation.
1114         $params = self::validate_parameters(self::get_calendar_upcoming_view_parameters(), [
1115             'courseid' => $courseid,
1116             'categoryid' => $categoryid,
1117         ]);
1119         $context = \context_user::instance($USER->id);
1120         self::validate_context($context);
1121         $PAGE->set_url('/calendar/');
1123         $calendar = \calendar_information::create(time(), $params['courseid'], $params['categoryid']);
1124         self::validate_context($calendar->context);
1126         list($data, $template) = calendar_get_view($calendar, 'upcoming');
1128         return $data;
1129     }
1131     /**
1132      * Returns description of method parameters.
1133      *
1134      * @return external_function_parameters
1135      */
1136     public static function get_calendar_upcoming_view_parameters() {
1137         return new external_function_parameters(
1138             [
1139                 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
1140                 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
1141             ]
1142         );
1143     }
1145     /**
1146      * Returns description of method result value.
1147      *
1148      * @return external_description
1149      */
1150     public static function get_calendar_upcoming_view_returns() {
1151         return \core_calendar\external\calendar_upcoming_exporter::get_read_structure();
1152     }