From 6688ae2ba20e93bed4b781650758b9630de215b1 Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Tue, 7 Nov 2017 05:47:18 +0000 Subject: [PATCH] MDL-60062 calendar: prevent drag and drop of override events --- .../classes/external/calendar_event_exporter.php | 18 ++++++++- calendar/classes/local/api.php | 8 +++- calendar/templates/month_detailed.mustache | 4 +- calendar/tests/local_api_test.php | 46 ++++++++++++++++++++++ mod/assign/lib.php | 4 ++ mod/assign/locallib.php | 9 ++++- mod/assign/tests/lib_test.php | 4 +- mod/quiz/lib.php | 7 +++- mod/quiz/tests/calendar_event_modified_test.php | 4 +- 9 files changed, 92 insertions(+), 12 deletions(-) diff --git a/calendar/classes/external/calendar_event_exporter.php b/calendar/classes/external/calendar_event_exporter.php index f3dd9ffd2ca..ad97c962745 100644 --- a/calendar/classes/external/calendar_event_exporter.php +++ b/calendar/classes/external/calendar_event_exporter.php @@ -74,6 +74,10 @@ class calendar_event_exporter extends event_exporter_base { 'type' => PARAM_TEXT, 'optional' => true ]; + $values['draggable'] = [ + 'type' => PARAM_BOOL, + 'default' => false + ]; return $values; } @@ -90,6 +94,10 @@ class calendar_event_exporter extends event_exporter_base { $values = parent::get_other_values($output); $event = $this->event; + // By default all events that can be edited are + // draggable. + $values['draggable'] = $values['canedit']; + if ($moduleproxy = $event->get_course_module()) { $modulename = $moduleproxy->get('modname'); $moduleid = $moduleproxy->get('id'); @@ -226,9 +234,17 @@ class calendar_event_exporter extends event_exporter_base { 'mod_' . $modname, 'core_calendar_get_valid_event_timestart_range', [$mapper->from_event_to_legacy_event($event), $moduleinstance], - [null, null] + [false, false] ); + // The callback will return false for either of the + // min or max cutoffs to indicate that there are no + // valid timestart values. In which case the event is + // not draggable. + if ($min === false || $max === false) { + return ['draggable' => false]; + } + if ($min) { $values = array_merge($values, $this->get_module_timestamp_min_limit($starttime, $min)); } diff --git a/calendar/classes/local/api.php b/calendar/classes/local/api.php index 1b81231c991..6da620e3bda 100644 --- a/calendar/classes/local/api.php +++ b/calendar/classes/local/api.php @@ -260,9 +260,15 @@ class api { 'mod_' . $event->get_course_module()->get('modname'), 'core_calendar_get_valid_event_timestart_range', [$legacyevent, $moduleinstance], - [null, null] + [false, false] ); + // If the callback returns false for either value it means that + // there is no valid time start range. + if ($min === false || $max === false) { + throw new \moodle_exception('The start day of this event can not be modified'); + } + if ($min && $legacyevent->timestart < $min[0]) { throw new \moodle_exception($min[1]); } diff --git a/calendar/templates/month_detailed.mustache b/calendar/templates/month_detailed.mustache index 0cb0d8e782a..9abff8e2c2a 100644 --- a/calendar/templates/month_detailed.mustache +++ b/calendar/templates/month_detailed.mustache @@ -87,7 +87,7 @@ {{^underway}}
  • + {{/draggable}}> diff --git a/calendar/tests/local_api_test.php b/calendar/tests/local_api_test.php index d97c08cac51..ff2e065fb96 100644 --- a/calendar/tests/local_api_test.php +++ b/calendar/tests/local_api_test.php @@ -1239,4 +1239,50 @@ class core_calendar_local_api_testcase extends advanced_testcase { $this->expectException('moodle_exception'); $newevent = \core_calendar\local\api::update_event_start_day($event, $newstartdate); } + + /** + * Updating the start day of an overridden event belonging to an activity + * should result in an exception. This is to prevent the drag and drop + * of override events. + * + * Note: This test uses the quiz activity because it requires + * module callbacks to be in place and override event support to test. + */ + public function test_update_event_start_day_activity_event_override() { + global $CFG, $DB; + require_once($CFG->dirroot . '/calendar/lib.php'); + require_once($CFG->dirroot . '/mod/quiz/lib.php'); + + $this->resetAfterTest(true); + $this->setAdminUser(); + $mapper = container::get_event_mapper(); + $timeopen = new DateTimeImmutable('2017-01-1T15:00:00+08:00'); + $newstartdate = new DateTimeImmutable('2016-02-2T10:00:00+08:00'); + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $quizgenerator = $generator->get_plugin_generator('mod_quiz'); + $quiz = $quizgenerator->create_instance([ + 'course' => $course->id, + 'timeopen' => $timeopen->getTimestamp(), + ]); + $event = create_event([ + 'courseid' => $course->id, + 'userid' => $user->id, + 'modulename' => 'quiz', + 'instance' => $quiz->id, + 'eventtype' => QUIZ_EVENT_TYPE_OPEN, + 'timestart' => $timeopen->getTimestamp() + ]); + $event = $mapper->from_legacy_event_to_event($event); + $record = (object) [ + 'quiz' => $quiz->id, + 'userid' => $user->id + ]; + + $DB->insert_record('quiz_overrides', $record); + + $this->expectException('moodle_exception'); + $newevent = \core_calendar\local\api::update_event_start_day($event, $newstartdate); + } } diff --git a/mod/assign/lib.php b/mod/assign/lib.php index 214362b5133..5eeccf327e4 100644 --- a/mod/assign/lib.php +++ b/mod/assign/lib.php @@ -1946,8 +1946,12 @@ function mod_assign_core_calendar_event_action_shows_item_count(calendar_event $ * [1506741172, 'The due date must be before the cutoff date'] * ] * + * If the event does not have a valid timestart range then [false, false] will + * be returned. + * * @param calendar_event $event The calendar event to get the time range for * @param stdClass $instance The module instance to get the range from + * @return array */ function mod_assign_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $instance) { global $CFG; diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index 61c6d9b54a7..ae24d0f7db8 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -1002,7 +1002,11 @@ class assign { * [1506741172, 'The due date must be before the cutoff date'] * ] * + * If the event does not have a valid timestart range then [false, false] will + * be returned. + * * @param calendar_event $event The calendar event to get the time range for + * @return array */ function get_valid_calendar_event_timestart_range(\calendar_event $event) { $instance = $this->get_instance(); @@ -1018,8 +1022,9 @@ class assign { // the only events that can be overridden, so we can save a DB // query if we don't bother checking other events. if ($this->is_override_calendar_event($event)) { - // This is an override event so we should ignore it. - return [null, null]; + // This is an override event so there is no valid timestart + // range to set it to. + return [false, false]; } if ($submissionsfromdate) { diff --git a/mod/assign/tests/lib_test.php b/mod/assign/tests/lib_test.php index 5e057eea08f..cadf7660141 100644 --- a/mod/assign/tests/lib_test.php +++ b/mod/assign/tests/lib_test.php @@ -883,8 +883,8 @@ class mod_assign_lib_testcase extends mod_assign_base_testcase { $DB->insert_record('assign_overrides', $record); list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); - $this->assertNull($min); - $this->assertNull($max); + $this->assertFalse($min); + $this->assertFalse($max); } /** diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 49aef8d3a34..9b2b227b8ff 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -2264,6 +2264,9 @@ function mod_quiz_get_completion_active_rule_descriptions($cm) { * If either value isn't set then null will be returned instead to * indicate that there is no cutoff for that value. * + * If the vent has no valid timestart range then [false, false] will + * be returned. This is the case for overriden events. + * * A minimum and maximum cutoff return value will look like: * [ * [1505704373, 'The date must be after this date'], @@ -2279,9 +2282,9 @@ function mod_quiz_core_calendar_get_valid_event_timestart_range(\calendar_event global $CFG, $DB; require_once($CFG->dirroot . '/mod/quiz/locallib.php'); - // No restrictions on override events. + // Overrides do not have a valid timestart range. if (quiz_is_overriden_calendar_event($event)) { - return [null, null]; + return [false, false]; } $mindate = null; diff --git a/mod/quiz/tests/calendar_event_modified_test.php b/mod/quiz/tests/calendar_event_modified_test.php index e514e047a60..4a4cfdf0ffc 100644 --- a/mod/quiz/tests/calendar_event_modified_test.php +++ b/mod/quiz/tests/calendar_event_modified_test.php @@ -416,8 +416,8 @@ class mod_quiz_calendar_event_modified_testcase extends advanced_testcase { list ($min, $max) = mod_quiz_core_calendar_get_valid_event_timestart_range($event, $quiz); - $this->assertNull($min); - $this->assertNull($max); + $this->assertFalse($min); + $this->assertFalse($max); } /** -- 2.11.4.GIT