diff --git a/calendar/classes/external/event_exporter_base.php b/calendar/classes/external/event_exporter_base.php index e975c9953d8..cc7ef38312c 100644 --- a/calendar/classes/external/event_exporter_base.php +++ b/calendar/classes/external/event_exporter_base.php @@ -193,7 +193,7 @@ class event_exporter_base extends exporter { $values['course'] = $coursesummaryexporter->export($output); } - $values['canedit'] = calendar_edit_event_allowed($legacyevent); + $values['canedit'] = calendar_edit_event_allowed($legacyevent, true); $values['candelete'] = calendar_delete_event_allowed($legacyevent); return $values; diff --git a/calendar/event.php b/calendar/event.php index 66c6a6f18b5..dad5fd58159 100644 --- a/calendar/event.php +++ b/calendar/event.php @@ -114,7 +114,7 @@ $formoptions = new stdClass; if ($eventid !== 0) { $title = get_string('editevent', 'calendar'); $event = calendar_event::load($eventid); - if (!calendar_edit_event_allowed($event)) { + if (!calendar_edit_event_allowed($event, true)) { print_error('nopermissions'); } $event->action = $action; diff --git a/calendar/externallib.php b/calendar/externallib.php index b7f7e55856d..94e74fdae86 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -811,6 +811,10 @@ class core_calendar_external extends external_api { $properties = $legacyevent->properties(true); } + if (!calendar_edit_event_allowed($legacyevent, true)) { + print_error('nopermissiontoupdatecalendar'); + } + $legacyevent->update($properties); $eventmapper = event_container::get_event_mapper(); @@ -947,7 +951,19 @@ class core_calendar_external extends external_api { self::validate_context($context); $vault = event_container::get_event_vault(); + $mapper = event_container::get_event_mapper(); $event = $vault->get_event_by_id($eventId); + + if (!$event) { + throw new \moodle_exception('Unable to find event with id ' . $eventId); + } + + $legacyevent = $mapper->from_event_to_legacy_event($event); + + if (!calendar_edit_event_allowed($legacyevent, true)) { + print_error('nopermissiontoupdatecalendar'); + } + $newdate = usergetdate($dayTimestamp); $startdatestring = implode('-', [$newdate['year'], $newdate['mon'], $newdate['mday']]); $startdate = new DateTimeImmutable($startdatestring); diff --git a/calendar/lib.php b/calendar/lib.php index bb61015ecc6..30cbaa42038 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -2435,9 +2435,10 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) { * Return the capability for editing calendar event. * * @param calendar_event $event event object + * @param bool $manualedit is the event being edited manually by the user * @return bool capability to edit event */ -function calendar_edit_event_allowed($event) { +function calendar_edit_event_allowed($event, $manualedit = false) { global $USER, $DB; // Must be logged in. @@ -2450,6 +2451,12 @@ function calendar_edit_event_allowed($event) { return false; } + if ($manualedit && !empty($event->modulename)) { + // A user isn't allowed to directly edit an event generated + // by a module. + return false; + } + // You cannot edit URL based calendar subscription events presently. if (!empty($event->subscriptionid)) { if (!empty($event->subscription->url)) { diff --git a/calendar/tests/externallib_test.php b/calendar/tests/externallib_test.php index a1919bc091b..bbf9f37c8ba 100644 --- a/calendar/tests/externallib_test.php +++ b/calendar/tests/externallib_test.php @@ -1332,8 +1332,8 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { } /** - * Updating the event start day should change the date value but leave - * the time of day unchanged. + * A user should not be able to edit an event that they don't have + * capabilities for. */ public function test_update_event_start_day_no_permission() { $generator = $this->getDataGenerator(); @@ -1370,4 +1370,48 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $result ); } + + /** + * A user should not be able to update a module event. + */ + public function test_update_event_start_day_module_event() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $moduleinstance = $generator->get_plugin_generator('mod_assign') + ->create_instance(['course' => $course->id]); + $roleid = $generator->create_role(); + $context = \context_course::instance($course->id); + $originalStartTime = new DateTimeImmutable('2017-01-1T15:00:00+08:00'); + $newStartDate = new DateTimeImmutable('2018-02-2T10:00:00+08:00'); + $expected = new DateTimeImmutable('2018-02-2T15:00:00+08:00'); + + $generator->role_assign($roleid, $user->id, $context->id); + $generator->enrol_user($user->id, $course->id); + + $this->setUser($user); + $this->resetAfterTest(true); + + $event = $this->create_calendar_event( + 'Test event', + $user->id, + 'user', + 0, + null, + [ + 'modulename' => 'assign', + 'instance' => $moduleinstance->id, + 'courseid' => $course->id, + 'timestart' => $originalStartTime->getTimestamp() + ] + ); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + $this->expectException('moodle_exception'); + $result = core_calendar_external::update_event_start_day($event->id, $newStartDate->getTimestamp()); + $result = external_api::clean_returnvalue( + core_calendar_external::update_event_start_day_returns(), + $result + ); + } }