MDL-57503 calendar: external function get action events by course
[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\external\events_exporter;
34 use \core_calendar\external\events_grouped_by_course_exporter;
35 use \core_calendar\external\events_related_objects_cache;
37 /**
38  * Calendar external functions
39  *
40  * @package    core_calendar
41  * @category   external
42  * @copyright  2012 Ankit Agarwal
43  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44  * @since Moodle 2.5
45  */
46 class core_calendar_external extends external_api {
49     /**
50      * Returns description of method parameters
51      *
52      * @return external_function_parameters
53      * @since Moodle 2.5
54      */
55     public static function delete_calendar_events_parameters() {
56         return new external_function_parameters(
57                 array('events' => new external_multiple_structure(
58                         new external_single_structure(
59                                 array(
60                                         'eventid' => new external_value(PARAM_INT, 'Event ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
61                                         'repeat'  => new external_value(PARAM_BOOL, 'Delete comeplete series if repeated event')
62                                 ), 'List of events to delete'
63                         )
64                     )
65                 )
66         );
67     }
69     /**
70      * Delete Calendar events
71      *
72      * @param array $eventids A list of event ids with repeat flag to delete
73      * @return null
74      * @since Moodle 2.5
75      */
76     public static function delete_calendar_events($events) {
77         global $CFG, $DB;
78         require_once($CFG->dirroot."/calendar/lib.php");
80         // Parameter validation.
81         $params = self::validate_parameters(self:: delete_calendar_events_parameters(), array('events' => $events));
83         $transaction = $DB->start_delegated_transaction();
85         foreach ($params['events'] as $event) {
86             $eventobj = \core_calendar\event::load($event['eventid']);
88             // Let's check if the user is allowed to delete an event.
89             if (!\core_calendar\api::can_edit_event($eventobj)) {
90                 throw new moodle_exception("nopermissions");
91             }
92             // Time to do the magic.
93             $eventobj->delete($event['repeat']);
94         }
96         // Everything done smoothly, let's commit.
97         $transaction->allow_commit();
99         return null;
100     }
102     /**
103      * Returns description of method result value
104      *
105      * @return external_description
106      * @since Moodle 2.5
107      */
108     public static function  delete_calendar_events_returns() {
109         return null;
110     }
112     /**
113      * Returns description of method parameters
114      *
115      * @return external_function_parameters
116      * @since Moodle 2.5
117      */
118     public static function get_calendar_events_parameters() {
119         return new external_function_parameters(
120                 array('events' => new external_single_structure(
121                             array(
122                                     'eventids' => new external_multiple_structure(
123                                             new external_value(PARAM_INT, 'event ids')
124                                             , 'List of event ids',
125                                             VALUE_DEFAULT, array(), NULL_ALLOWED
126                                                 ),
127                                     'courseids' => new external_multiple_structure(
128                                             new external_value(PARAM_INT, 'course ids')
129                                             , 'List of course ids for which events will be returned',
130                                             VALUE_DEFAULT, array(), NULL_ALLOWED
131                                                 ),
132                                     'groupids' => new external_multiple_structure(
133                                             new external_value(PARAM_INT, 'group ids')
134                                             , 'List of group ids for which events should be returned',
135                                             VALUE_DEFAULT, array(), NULL_ALLOWED
136                                                 )
137                             ), 'Event details', VALUE_DEFAULT, array()),
138                     'options' => new external_single_structure(
139                             array(
140                                     'userevents' => new external_value(PARAM_BOOL,
141                                              "Set to true to return current user's user events",
142                                              VALUE_DEFAULT, true, NULL_ALLOWED),
143                                     'siteevents' => new external_value(PARAM_BOOL,
144                                              "Set to true to return global events",
145                                              VALUE_DEFAULT, true, NULL_ALLOWED),
146                                     'timestart' => new external_value(PARAM_INT,
147                                              "Time from which events should be returned",
148                                              VALUE_DEFAULT, 0, NULL_ALLOWED),
149                                     'timeend' => new external_value(PARAM_INT,
150                                              "Time to which the events should be returned. We treat 0 and null as no end",
151                                              VALUE_DEFAULT, 0, NULL_ALLOWED),
152                                     'ignorehidden' => new external_value(PARAM_BOOL,
153                                              "Ignore hidden events or not",
154                                              VALUE_DEFAULT, true, NULL_ALLOWED),
156                             ), 'Options', VALUE_DEFAULT, array())
157                 )
158         );
159     }
161     /**
162      * Get Calendar events
163      *
164      * @param array $events A list of events
165      * @param array $options various options
166      * @return array Array of event details
167      * @since Moodle 2.5
168      */
169     public static function get_calendar_events($events = array(), $options = array()) {
170         global $SITE, $DB, $USER, $CFG;
171         require_once($CFG->dirroot."/calendar/lib.php");
173         // Parameter validation.
174         $params = self::validate_parameters(self::get_calendar_events_parameters(), array('events' => $events, 'options' => $options));
175         $funcparam = array('courses' => array(), 'groups' => array());
176         $hassystemcap = has_capability('moodle/calendar:manageentries', context_system::instance());
177         $warnings = array();
179         // Let us findout courses that we can return events from.
180         if (!$hassystemcap) {
181             $courses = enrol_get_my_courses('id');
182             $courses = array_keys($courses);
183             foreach ($params['events']['courseids'] as $id) {
184                try {
185                     $context = context_course::instance($id);
186                     self::validate_context($context);
187                     $funcparam['courses'][] = $id;
188                 } catch (Exception $e) {
189                     $warnings[] = array(
190                         'item' => 'course',
191                         'itemid' => $id,
192                         'warningcode' => 'nopermissions',
193                         'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
194                     );
195                 }
196             }
197         } else {
198             $courses = $params['events']['courseids'];
199             $funcparam['courses'] = $courses;
200         }
202         // Let us findout groups that we can return events from.
203         if (!$hassystemcap) {
204             $groups = groups_get_my_groups();
205             $groups = array_keys($groups);
206             foreach ($params['events']['groupids'] as $id) {
207                 if (in_array($id, $groups)) {
208                     $funcparam['groups'][] = $id;
209                 } else {
210                     $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this group');
211                 }
212             }
213         } else {
214             $groups = $params['events']['groupids'];
215             $funcparam['groups'] = $groups;
216         }
218         // Do we need user events?
219         if (!empty($params['options']['userevents'])) {
220             $funcparam['users'] = array($USER->id);
221         } else {
222             $funcparam['users'] = false;
223         }
225         // Do we need site events?
226         if (!empty($params['options']['siteevents'])) {
227             $funcparam['courses'][] = $SITE->id;
228         }
230         // We treat 0 and null as no end.
231         if (empty($params['options']['timeend'])) {
232             $params['options']['timeend'] = PHP_INT_MAX;
233         }
235         // Event list does not check visibility and permissions, we'll check that later.
236         $eventlist = \core_calendar\api::get_events($params['options']['timestart'], $params['options']['timeend'],
237             $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, $params['options']['ignorehidden']);
239         // WS expects arrays.
240         $events = array();
242         // We need to get events asked for eventids.
243         if ($eventsbyid = \core_calendar\api::get_events_by_id($params['events']['eventids'])) {
244             $eventlist += $eventsbyid;
245         }
247         foreach ($eventlist as $eventid => $eventobj) {
248             $event = (array) $eventobj;
249             // Description formatting.
250             $calendareventobj = new \core_calendar\event($event);
251             list($event['description'], $event['format']) = $calendareventobj->format_external_text();
253             if ($hassystemcap) {
254                 // User can see everything, no further check is needed.
255                 $events[$eventid] = $event;
256             } else if (!empty($eventobj->modulename)) {
257                 $cm = get_coursemodule_from_instance($eventobj->modulename, $eventobj->instance);
258                 if (\core_availability\info_module::is_user_visible($cm, 0, false)) {
259                     $events[$eventid] = $event;
260                 }
261             } else {
262                 // Can the user actually see this event?
263                 $eventobj = \core_calendar\event::load($eventobj);
264                 if (($eventobj->courseid == $SITE->id) ||
265                             (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) ||
266                             (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) ||
267                             ($USER->id == $eventobj->userid) ||
268                             (\core_calendar\api::can_edit_event($eventid))) {
269                     $events[$eventid] = $event;
270                 } else {
271                     $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event');
272                 }
273             }
274         }
275         return array('events' => $events, 'warnings' => $warnings);
276     }
278     /**
279      * Returns description of method result value
280      *
281      * @return external_description
282      * @since Moodle 2.5
283      */
284     public static function  get_calendar_events_returns() {
285         return new external_single_structure(array(
286                 'events' => new external_multiple_structure( new external_single_structure(
287                         array(
288                             'id' => new external_value(PARAM_INT, 'event id'),
289                             'name' => new external_value(PARAM_TEXT, 'event name'),
290                             'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL, null, NULL_ALLOWED),
291                             'format' => new external_format_value('description'),
292                             'courseid' => new external_value(PARAM_INT, 'course id'),
293                             'groupid' => new external_value(PARAM_INT, 'group id'),
294                             'userid' => new external_value(PARAM_INT, 'user id'),
295                             'repeatid' => new external_value(PARAM_INT, 'repeat id'),
296                             'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL, null, NULL_ALLOWED),
297                             'instance' => new external_value(PARAM_INT, 'instance id'),
298                             'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
299                             'timestart' => new external_value(PARAM_INT, 'timestart'),
300                             'timeduration' => new external_value(PARAM_INT, 'time duration'),
301                             'visible' => new external_value(PARAM_INT, 'visible'),
302                             'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, null, NULL_NOT_ALLOWED),
303                             'sequence' => new external_value(PARAM_INT, 'sequence'),
304                             'timemodified' => new external_value(PARAM_INT, 'time modified'),
305                             'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL, null, NULL_ALLOWED),
306                         ), 'event')
307                  ),
308                  'warnings' => new external_warnings()
309                 )
310         );
311     }
313     /**
314      * Returns description of method parameters.
315      *
316      * @since Moodle 3.3
317      * @return external_function_parameters
318      */
319     public static function get_calendar_action_events_by_timesort_parameters() {
320         return new external_function_parameters(
321             array(
322                 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, 0),
323                 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
324                 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
325                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20)
326             )
327         );
328     }
330     /**
331      * Get calendar action events based on the timesort value.
332      *
333      * @since Moodle 3.3
334      * @param null|int $timesortfrom Events after this time (inclusive)
335      * @param null|int $timesortto Events before this time (inclusive)
336      * @param null|int $aftereventid Get events with ids greater than this one
337      * @param int $limitnum Limit the number of results to this value
338      * @return array
339      */
340     public static function get_calendar_action_events_by_timesort($timesortfrom = 0, $timesortto = null,
341                                                        $aftereventid = 0, $limitnum = 20) {
342         global $CFG, $PAGE, $USER;
344         require_once($CFG->dirroot . '/calendar/lib.php');
346         $user = null;
347         $params = self::validate_parameters(
348             self::get_calendar_action_events_by_timesort_parameters(),
349             [
350                 'timesortfrom' => $timesortfrom,
351                 'timesortto' => $timesortto,
352                 'aftereventid' => $aftereventid,
353                 'limitnum' => $limitnum,
354             ]
355         );
356         $context = \context_user::instance($USER->id);
357         self::validate_context($context);
359         if (empty($params['aftereventid'])) {
360             $params['aftereventid'] = null;
361         }
363         $renderer = $PAGE->get_renderer('core_calendar');
364         $events = local_api::get_action_events_by_timesort(
365             $params['timesortfrom'],
366             $params['timesortto'],
367             $params['aftereventid'],
368             $params['limitnum']
369         );
371         $exportercache = new events_related_objects_cache($events);
372         $exporter = new events_exporter($events, ['cache' => $exportercache]);
374         return $exporter->export($renderer);
375     }
377     /**
378      * Returns description of method result value.
379      *
380      * @since Moodle 3.3
381      * @return external_description
382      */
383     public static function get_calendar_action_events_by_timesort_returns() {
384         return events_exporter::get_read_structure();
385     }
387     /**
388      * Returns description of method parameters.
389      *
390      * @return external_function_parameters
391      */
392     public static function get_calendar_action_events_by_course_parameters() {
393         return new external_function_parameters(
394             array(
395                 'courseid' => new external_value(PARAM_INT, 'Course id'),
396                 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null),
397                 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null),
398                 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0),
399                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20)
400             )
401         );
402     }
404     /**
405      * Get calendar action events for the given course.
406      *
407      * @since Moodle 3.3
408      * @param int $courseid Only events in this course
409      * @param null|int $timesortfrom Events after this time (inclusive)
410      * @param null|int $timesortto Events before this time (inclusive)
411      * @param null|int $aftereventid Get events with ids greater than this one
412      * @param int $limitnum Limit the number of results to this value
413      * @return array
414      */
415     public static function get_calendar_action_events_by_course(
416         $courseid, $timesortfrom = null, $timesortto = null, $aftereventid = 0, $limitnum = 20) {
418         global $CFG, $PAGE, $USER;
420         require_once($CFG->dirroot . '/calendar/lib.php');
422         $user = null;
423         $params = self::validate_parameters(
424             self::get_calendar_action_events_by_course_parameters(),
425             [
426                 'courseid' => $courseid,
427                 'timesortfrom' => $timesortfrom,
428                 'timesortto' => $timesortto,
429                 'aftereventid' => $aftereventid,
430                 'limitnum' => $limitnum,
431             ]
432         );
433         $context = \context_user::instance($USER->id);
434         self::validate_context($context);
436         if (empty($params['aftereventid'])) {
437             $params['aftereventid'] = null;
438         }
440         $courses = enrol_get_my_courses('*', 'visible DESC,sortorder ASC', 0, [$courseid]);
441         $courses = array_values($courses);
443         if (empty($courses)) {
444             return [];
445         }
447         $course = $courses[0];
448         $renderer = $PAGE->get_renderer('core_calendar');
449         $events = local_api::get_action_events_by_course(
450             $course,
451             $params['timesortfrom'],
452             $params['timesortto'],
453             $params['aftereventid'],
454             $params['limitnum']
455         );
457         $exportercache = new events_related_objects_cache($events, $courses);
458         $exporter = new events_exporter($events, ['cache' => $exportercache]);
460         return $exporter->export($renderer);
461     }
463     /**
464      * Returns description of method result value.
465      *
466      * @return external_description
467      */
468     public static function get_calendar_action_events_by_course_returns() {
469         return events_exporter::get_read_structure();
470     }
472     /**
473      * Returns description of method parameters.
474      *
475      * @return external_function_parameters.
476      * @since Moodle 2.5
477      */
478     public static function create_calendar_events_parameters() {
479         // Userid is always current user, so no need to get it from client.
480         // Module based calendar events are not allowed here. Hence no need of instance and modulename.
481         // subscription id and uuid is not allowed as this is not an ical api.
482         return new external_function_parameters(
483                 array('events' => new external_multiple_structure(
484                         new external_single_structure(
485                             array(
486                                 'name' => new external_value(PARAM_TEXT, 'event name', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
487                                 'description' => new external_value(PARAM_RAW, 'Description', VALUE_DEFAULT, null, NULL_ALLOWED),
488                                 'format' => new external_format_value('description', VALUE_DEFAULT),
489                                 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
490                                 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
491                                 'repeats' => new external_value(PARAM_INT, 'number of repeats', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
492                                 'eventtype' => new external_value(PARAM_TEXT, 'Event type', VALUE_DEFAULT, 'user', NULL_NOT_ALLOWED),
493                                 'timestart' => new external_value(PARAM_INT, 'timestart', VALUE_DEFAULT, time(), NULL_NOT_ALLOWED),
494                                 'timeduration' => new external_value(PARAM_INT, 'time duration', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
495                                 'visible' => new external_value(PARAM_INT, 'visible', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
496                                 'sequence' => new external_value(PARAM_INT, 'sequence', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
497                             ), 'event')
498                 )
499             )
500         );
501     }
503     /**
504      * Delete Calendar events.
505      *
506      * @param array $events A list of events to create.
507      * @return array array of events created.
508      * @since Moodle 2.5
509      * @throws moodle_exception if user doesnt have the permission to create events.
510      */
511     public static function create_calendar_events($events) {
512         global $CFG, $DB, $USER;
513         require_once($CFG->dirroot."/calendar/lib.php");
515         // Parameter validation.
516         $params = self::validate_parameters(self::create_calendar_events_parameters(), array('events' => $events));
518         $transaction = $DB->start_delegated_transaction();
519         $return = array();
520         $warnings = array();
522         foreach ($params['events'] as $event) {
524             // Let us set some defaults.
525             $event['userid'] = $USER->id;
526             $event['modulename'] = '';
527             $event['instance'] = 0;
528             $event['subscriptionid'] = null;
529             $event['uuid']= '';
530             $event['format'] = external_validate_format($event['format']);
531             if ($event['repeats'] > 0) {
532                 $event['repeat'] = 1;
533             } else {
534                 $event['repeat'] = 0;
535             }
537             $eventobj = new \core_calendar\event($event);
539             // Let's check if the user is allowed to delete an event.
540             if (!\core_calendar\api::can_add_event($eventobj)) {
541                 $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event');
542                 continue;
543             }
544             // Let's create the event.
545             $var = $eventobj->create($event);
546             $var = (array)$var->properties();
547             if ($event['repeat']) {
548                 $children = $DB->get_records('event', array('repeatid' => $var['id']));
549                 foreach ($children as $child) {
550                     $return[] = (array) $child;
551                 }
552             } else {
553                 $return[] = $var;
554             }
555         }
557         // Everything done smoothly, let's commit.
558         $transaction->allow_commit();
559         return array('events' => $return, 'warnings' => $warnings);
560     }
562     /**
563      * Returns description of method result value.
564      *
565      * @return external_description.
566      * @since Moodle 2.5
567      */
568     public static function  create_calendar_events_returns() {
569             return new external_single_structure(
570                     array(
571                         'events' => new external_multiple_structure( new external_single_structure(
572                                 array(
573                                     'id' => new external_value(PARAM_INT, 'event id'),
574                                     'name' => new external_value(PARAM_TEXT, 'event name'),
575                                     'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL),
576                                     'format' => new external_format_value('description'),
577                                     'courseid' => new external_value(PARAM_INT, 'course id'),
578                                     'groupid' => new external_value(PARAM_INT, 'group id'),
579                                     'userid' => new external_value(PARAM_INT, 'user id'),
580                                     'repeatid' => new external_value(PARAM_INT, 'repeat id', VALUE_OPTIONAL),
581                                     'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL),
582                                     'instance' => new external_value(PARAM_INT, 'instance id'),
583                                     'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
584                                     'timestart' => new external_value(PARAM_INT, 'timestart'),
585                                     'timeduration' => new external_value(PARAM_INT, 'time duration'),
586                                     'visible' => new external_value(PARAM_INT, 'visible'),
587                                     'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
588                                     'sequence' => new external_value(PARAM_INT, 'sequence'),
589                                     'timemodified' => new external_value(PARAM_INT, 'time modified'),
590                                     'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL),
591                                 ), 'event')
592                         ),
593                       'warnings' => new external_warnings()
594                     )
595             );
596     }