diff --git a/calendar/classes/local/event/forms/create.php b/calendar/classes/local/event/forms/create.php index 3e8ed78614d..a79e3dcb03d 100644 --- a/calendar/classes/local/event/forms/create.php +++ b/calendar/classes/local/event/forms/create.php @@ -44,6 +44,10 @@ class create extends \moodleform { $haserror = !empty($this->_customdata['haserror']); $eventtypes = calendar_get_all_allowed_types(); + if (empty($eventtypes)) { + print_error('nopermissiontoupdatecalendar'); + } + $mform->setDisableShortforms(); $mform->disable_form_change_checker(); @@ -93,6 +97,12 @@ class create extends \moodleform { $errors = parent::validation($data, $files); $coursekey = isset($data['groupcourseid']) ? 'groupcourseid' : 'courseid'; + $eventtypes = calendar_get_all_allowed_types(); + $eventtype = isset($data['eventtype']) ? $data['eventtype'] : null; + + if (empty($eventtype) || !isset($eventtypes[$eventtype])) { + $errors['eventtype'] = get_string('invalideventtype', 'calendar'); + } if (isset($data[$coursekey]) && $data[$coursekey] > 0) { if ($course = $DB->get_record('course', ['id' => $data[$coursekey]])) { diff --git a/calendar/classes/local/event/mappers/create_update_form_mapper.php b/calendar/classes/local/event/mappers/create_update_form_mapper.php index bedcccc728f..b0bb691988a 100644 --- a/calendar/classes/local/event/mappers/create_update_form_mapper.php +++ b/calendar/classes/local/event/mappers/create_update_form_mapper.php @@ -90,7 +90,11 @@ class create_update_form_mapper implements create_update_form_mapper_interface { // Default course id if none is set. if (!isset($properties->courseid)) { - $properties->courseid = 0; + if ($properties->eventtype === 'site') { + $properties->courseid = SITEID; + } else { + $properties->courseid = 0; + } } // Decode the form fields back into valid event property. diff --git a/calendar/tests/externallib_test.php b/calendar/tests/externallib_test.php index 8098f72608b..4537acfddbd 100644 --- a/calendar/tests/externallib_test.php +++ b/calendar/tests/externallib_test.php @@ -1414,4 +1414,785 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $result ); } + + /** + * Submit a request where the time duration until is earlier than the time + * start in order to get a validation error from the server. + */ + public function test_submit_create_update_form_validation_error() { + $user = $this->getDataGenerator()->create_user(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->sub($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'user', + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + + $querystring = http_build_query($formdata, '', '&'); + + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $this->assertTrue($result['validationerror']); + } + + /** + * A user with the moodle/calendar:manageownentries capability at the + * system context should be able to create a user event. + */ + public function test_submit_create_update_form_create_user_event() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $roleid = $generator->create_role(); + $context = \context_system::instance(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'user', + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->role_assign($roleid, $user->id, $context->id); + assign_capability('moodle/calendar:manageownentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + } + + /** + * A user without the moodle/calendar:manageownentries capability at the + * system context should not be able to create a user event. + */ + public function test_submit_create_update_form_create_user_event_no_permission() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $roleid = $generator->create_role(); + $context = \context_system::instance(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'user', + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->role_assign($roleid, $user->id, $context->id); + assign_capability('moodle/calendar:manageownentries', CAP_PROHIBIT, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $this->expectException('moodle_exception'); + + external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + } + + /** + * A user with the moodle/calendar:manageentries capability at the + * site course context should be able to create a site event. + */ + public function test_submit_create_update_form_create_site_event() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $context = context_course::instance(SITEID); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'site', + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + } + + /** + * A user without the moodle/calendar:manageentries capability at the + * site course context should not be able to create a site event. + */ + public function test_submit_create_update_form_create_site_event_no_permission() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $context = context_course::instance(SITEID); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'site', + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $this->assertTrue($result['validationerror']); + } + + /** + * A user that has the moodle/calendar:manageentries in a course that they + * are enrolled in should be able to create a course event in that course. + */ + public function test_submit_create_update_form_create_course_event() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'course', + 'courseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + $this->assertEquals($formdata['courseid'], $event['course']['id']); + } + + /** + * A user without the moodle/calendar:manageentries capability in a course + * that they are enrolled in should not be able to create a course event in that course. + */ + public function test_submit_create_update_form_create_course_event_no_permission() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'course', + 'courseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $this->assertTrue($result['validationerror']); + } + + /** + * A user should not be able to create an event for a course that they are + * not enrolled in. + */ + public function test_submit_create_update_form_create_course_event_not_enrolled() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $course2 = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'course', + 'courseid' => $course2->id, // Not enrolled. + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $this->expectException('moodle_exception'); + + external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + } + + /** + * A user should be able to create an event for a group that they are a member of in + * a course in which they are enrolled and have the moodle/calendar:manageentries capability. + */ + public function test_submit_create_update_form_create_group_event_group_member_manage_course() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $group = $generator->create_group(array('courseid' => $course->id)); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'group', + 'groupid' => "{$course->id}-{$group->id}", // The form format. + 'groupcourseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + $generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + $this->assertEquals($group->id, $event['groupid']); + } + + /** + * A user should be able to create an event for a group that they are a member of in + * a course in which they are enrolled and have the moodle/calendar:managegroupentries capability. + */ + public function test_submit_create_update_form_create_group_event_group_member_manage_group_entries() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $group = $generator->create_group(array('courseid' => $course->id)); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'group', + 'groupid' => "{$course->id}-{$group->id}", // The form format. + 'groupcourseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + $generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]); + + assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); + assign_capability('moodle/calendar:managegroupentries', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + $this->assertEquals($group->id, $event['groupid']); + } + + /** + * A user should be able to create an event for any group in a course in which + * they are enrolled and have the moodle/site:accessallgroups capability. + */ + public function test_submit_create_update_form_create_group_event_access_all_groups() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $group = $generator->create_group(array('courseid' => $course->id)); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'group', + 'groupid' => "{$course->id}-{$group->id}", // The form format. + 'groupcourseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $event = $result['event']; + $this->assertEquals($user->id, $event['userid']); + $this->assertEquals($formdata['eventtype'], $event['eventtype']); + $this->assertEquals($formdata['name'], $event['name']); + $this->assertEquals($group->id, $event['groupid']); + } + + /** + * A user should not be able to create an event for any group that they are not a + * member of in a course in which they are enrolled but don't have the + * moodle/site:accessallgroups capability. + */ + public function test_submit_create_update_form_create_group_event_non_member_no_permission() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $group = $generator->create_group(array('courseid' => $course->id)); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $timestart = new DateTime(); + $interval = new DateInterval("P1D"); // One day. + $timedurationuntil = new DateTime(); + $timedurationuntil->add($interval); + $formdata = [ + 'id' => 0, + 'userid' => $user->id, + 'modulename' => '', + 'instance' => 0, + 'visible' => 1, + 'name' => 'Test', + 'timestart' => [ + 'day' => $timestart->format('j'), + 'month' => $timestart->format('n'), + 'year' => $timestart->format('Y'), + 'hour' => $timestart->format('G'), + 'minute' => 0, + ], + 'eventtype' => 'group', + 'groupid' => "{$course->id}-{$group->id}", // The form format. + 'groupcourseid' => $course->id, + 'description' => [ + 'text' => '', + 'format' => 1, + ], + 'duration' => 1, + 'timedurationuntil' => [ + 'day' => $timedurationuntil->format('j'), + 'month' => $timedurationuntil->format('n'), + 'year' => $timedurationuntil->format('Y'), + 'hour' => $timedurationuntil->format('G'), + 'minute' => 0, + ] + ]; + + $formdata = \core_calendar\local\event\forms\create::mock_generate_submit_keys($formdata); + $querystring = http_build_query($formdata, '', '&'); + + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); + assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $roleid, $context, true); + + $user->ignoresesskey = true; + $this->resetAfterTest(true); + $this->setUser($user); + + $result = external_api::clean_returnvalue( + core_calendar_external::submit_create_update_form_returns(), + core_calendar_external::submit_create_update_form($querystring) + ); + + $this->assertTrue($result['validationerror']); + } } diff --git a/lang/en/calendar.php b/lang/en/calendar.php index dd271125d62..eccd23348ab 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -148,6 +148,7 @@ $string['importfromurl'] = 'Calendar URL'; $string['importfrominstructions'] = 'Please provide either a URL to a remote calendar, or upload a file.'; $string['invalidtimedurationminutes'] = 'The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.'; $string['invalidtimedurationuntil'] = 'The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.'; +$string['invalideventtype'] = 'The event type you have selected is invalid.'; $string['iwanttoexport'] = 'Export'; $string['less'] = 'Less'; $string['managesubscriptions'] = 'Manage subscriptions'; diff --git a/lib/formslib.php b/lib/formslib.php index 66527b655b3..0a2293a6af9 100644 --- a/lib/formslib.php +++ b/lib/formslib.php @@ -1390,6 +1390,27 @@ abstract class moodleform { $_POST = $simulatedsubmitteddata; } } + + /** + * Used by tests to generate valid submit keys for moodle forms that are + * submitted with ajax data. + * + * @throws \moodle_exception If called outside unit test environment + * @param array $data Existing form data you wish to add the keys to. + * @return array + */ + public static function mock_generate_submit_keys($data = []) { + if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) { + throw new \moodle_exception("This function can only be used for unit testing."); + } + + $formidentifier = get_called_class(); + $formidentifier = str_replace('\\', '_', $formidentifier); // See MDL-56233 for more information. + $data['sesskey'] = sesskey(); + $data['_qf__' . $formidentifier] = 1; + + return $data; + } } /**