MDL-60058 assign: allow update of assign calendar action events

This commit is contained in:
Ryan Wyllie
2017-10-13 06:18:43 +00:00
parent 4c1145009a
commit f4c2156189
3 changed files with 1130 additions and 0 deletions
+144
View File
@@ -1920,3 +1920,147 @@ function mod_assign_core_calendar_event_action_shows_item_count(calendar_event $
// For mod_assign, item count should be shown if the event type is 'gradingdue' and there is one or more item count.
return in_array($event->eventtype, $eventtypesshowingitemcount) && $itemcount > 0;
}
/**
* This function calculates the minimum and maximum cutoff values for the timestart of
* the given event.
*
* It will return an array with two values, the first being the minimum cutoff value and
* the second being the maximum cutoff value. Either or both values can be null, which
* indicates there is no minimum or maximum, respectively.
*
* If a cutoff is required then the function must return an array containing the cutoff
* timestamp and error string to display to the user if the cutoff value is violated.
*
* A minimum and maximum cutoff return value will look like:
* [
* [1505704373, 'The due date must be after the sbumission start date'],
* [1506741172, 'The due date must be before the cutoff date']
* ]
*
* @param calendar_event $event The calendar event to get the time range for
* @param stdClass|null $instance The module instance to get the range from
*/
function mod_assign_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $instance = null) {
global $DB;
if (!$instance) {
$instance = $DB->get_record('assign', ['id' => $event->instance]);
}
$coursemodule = get_coursemodule_from_instance('assign',
$event->instance,
$event->courseid,
false,
MUST_EXIST);
if (empty($coursemodule)) {
// If we don't have a course module yet then it likely means
// the activity is still being set up. In this case there is
// nothing for us to do anyway.
return;
}
$context = context_module::instance($coursemodule->id);
$assign = new assign($context, null, null);
$assign->set_instance($instance);
return $assign->get_valid_calendar_event_timestart_range($event);
}
/**
* This function will check that the given event is valid for it's
* corresponding assign module instance.
*
* An exception is thrown if the event fails validation.
*
* @throws \moodle_exception
* @param \calendar_event $event
* @return bool
*/
function mod_assign_core_calendar_validate_event_timestart(\calendar_event $event) {
global $DB;
if (!isset($event->instance)) {
return;
}
// We need to read from the DB directly because course module may
// currently be getting created so it won't be in mod info yet.
$instance = $DB->get_record('assign', ['id' => $event->instance], '*', MUST_EXIST);
$timestart = $event->timestart;
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
if ($min && $timestart < $min[0]) {
throw new \moodle_exception($min[1]);
}
if ($max && $timestart > $max[0]) {
throw new \moodle_exception($max[1]);
}
}
/**
* This function will update the assign module according to the
* event that has been modified.
*
* @throws \moodle_exception
* @param \calendar_event $event
*/
function mod_assign_core_calendar_event_timestart_updated(\calendar_event $event) {
global $DB;
if (empty($event->instance) || $event->modulename != 'assign') {
return;
}
$coursemodule = get_coursemodule_from_instance('assign',
$event->instance,
$event->courseid,
false,
MUST_EXIST);
if (empty($coursemodule)) {
// If we don't have a course module yet then it likely means
// the activity is still being set up. In this case there is
// nothing for us to do anyway.
return;
}
$context = context_module::instance($coursemodule->id);
// The user does not have the capability to modify this activity.
if (!has_capability('moodle/course:manageactivities', $context)) {
return;
}
$assign = new assign($context, $coursemodule, null);
$modified = false;
if ($event->eventtype == ASSIGN_EVENT_TYPE_DUE) {
// This check is in here because due date events are currently
// the only events that can be overridden, so we can save a DB
// query if we don't bother checking other events.
if ($assign->is_override_calendar_event($event)) {
// This is an override event so we should ignore it.
return;
}
$instance = $assign->get_instance();
$newduedate = $event->timestart;
if ($newduedate != $instance->duedate) {
$instance->duedate = $newduedate;
$instance->timemodified = time();
$modified = true;
}
}
if ($modified) {
// Persist the assign instance changes.
$DB->update_record('assign', $instance);
$assign->update_calendar($coursemodule->id);
$event = \core\event\course_module_updated::create_from_cm($coursemodule, $context);
$event->trigger();
}
}
+121
View File
@@ -943,6 +943,127 @@ class assign {
);
}
/**
* Check if the given calendar_event is either a user or group override
* event.
*
* @return bool
*/
public function is_override_calendar_event(\calendar_event $event) {
global $DB;
if (!isset($event->modulename)) {
return false;
}
if ($event->modulename != 'assign') {
return false;
}
if (!isset($event->instance)) {
return false;
}
if (!isset($event->userid) && !isset($event->groupid)) {
return false;
}
$overrideparams = [
'assignid' => $event->instance
];
if (isset($event->groupid)) {
$overrideparams['groupid'] = $event->groupid;
} else if (isset($event->userid)) {
$overrideparams['userid'] = $event->userid;
}
if ($DB->get_record('assign_overrides', $overrideparams)) {
return true;
} else {
return false;
}
}
/**
* This function calculates the minimum and maximum cutoff values for the timestart of
* the given event.
*
* It will return an array with two values, the first being the minimum cutoff value and
* the second being the maximum cutoff value. Either or both values can be null, which
* indicates there is no minimum or maximum, respectively.
*
* If a cutoff is required then the function must return an array containing the cutoff
* timestamp and error string to display to the user if the cutoff value is violated.
*
* A minimum and maximum cutoff return value will look like:
* [
* [1505704373, 'The due date must be after the sbumission start date'],
* [1506741172, 'The due date must be before the cutoff date']
* ]
*
* @param calendar_event $event The calendar event to get the time range for
*/
function get_valid_calendar_event_timestart_range(\calendar_event $event) {
$instance = $this->get_instance();
$submissionsfromdate = $instance->allowsubmissionsfromdate;
$cutoffdate = $instance->cutoffdate;
$duedate = $instance->duedate;
$gradingduedate = $instance->gradingduedate;
$mindate = null;
$maxdate = null;
if ($event->eventtype == ASSIGN_EVENT_TYPE_DUE) {
// This check is in here because due date events are currently
// 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];
}
if ($submissionsfromdate) {
$mindate = [
$submissionsfromdate,
get_string('duedatevalidation', 'assign'),
];
}
if ($cutoffdate) {
$maxdate = [
$cutoffdate,
get_string('cutoffdatevalidation', 'assign'),
];
}
if ($gradingduedate) {
// If we don't have a cutoff date or we've got a grading due date
// that is earlier than the cutoff then we should use that as the
// upper limit for the due date.
if (!$cutoffdate || $gradingduedate < $cutoffdate) {
$maxdate = [
$gradingduedate,
get_string('gradingdueduedatevalidation', 'assign'),
];
}
}
} else if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
if ($duedate) {
$mindate = [
$duedate,
get_string('gradingdueduedatevalidation', 'assign'),
];
} else if ($submissionsfromdate) {
$mindate = [
$submissionsfromdate,
get_string('gradingduefromdatevalidation', 'assign'),
];
}
}
return [$mindate, $maxdate];
}
/**
* Actual implementation of the reset course functionality, delete all the
* assignment submissions for course $data->courseid.
+865
View File
@@ -31,6 +31,9 @@ require_once($CFG->dirroot . '/mod/assign/lib.php');
require_once($CFG->dirroot . '/mod/assign/locallib.php');
require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
use \core_calendar\local\api as calendar_local_api;
use \core_calendar\local\event\container as calendar_event_container;
/**
* Unit tests for (some of) mod/assign/lib.php.
*
@@ -684,4 +687,866 @@ class mod_assign_lib_testcase extends mod_assign_base_testcase {
$this->assertEquals($student0grade->grade, 5);
$this->assertEquals($student1grade->grade, ASSIGN_GRADE_NOT_SET);
}
/**
* Return false when there are not overrides for this assign instance.
*/
public function test_assign_is_override_calendar_event_no_override() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$userid = 1234;
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$event = new \calendar_event((object)[
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid
]);
$this->assertFalse($assign->is_override_calendar_event($event));
}
/**
* Return false if the given event isn't an assign module event.
*/
public function test_assign_is_override_calendar_event_no_nodule_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$userid = $this->students[0]->id;
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$event = new \calendar_event((object)[
'userid' => $userid
]);
$this->assertFalse($assign->is_override_calendar_event($event));
}
/**
* Return false if there is overrides for this use but they belong to another assign
* instance.
*/
public function test_assign_is_override_calendar_event_different_assign_instance() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$userid = 1234;
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$assign2 = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid
]);
$record = (object) [
'assignid' => $assign2->get_instance()->id,
'userid' => $userid
];
$DB->insert_record('assign_overrides', $record);
$this->assertFalse($assign->is_override_calendar_event($event));
}
/**
* Return true if there is a user override for this event and assign instance.
*/
public function test_assign_is_override_calendar_event_user_override() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$userid = 1234;
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid
]);
$record = (object) [
'assignid' => $instance->id,
'userid' => $userid
];
$DB->insert_record('assign_overrides', $record);
$this->assertTrue($assign->is_override_calendar_event($event));
}
/**
* Return true if there is a group override for the event and assign instance.
*/
public function test_assign_is_override_calendar_event_group_override() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$group = $this->getDataGenerator()->create_group(array('courseid' => $instance->course));
$groupid = $group->id;
$event = new \calendar_event((object) [
'modulename' => 'assign',
'instance' => $instance->id,
'groupid' => $groupid
]);
$record = (object) [
'assignid' => $instance->id,
'groupid' => $groupid
];
$DB->insert_record('assign_overrides', $record);
$this->assertTrue($assign->is_override_calendar_event($event));
}
/**
* Unknown event types should not have any limit restrictions returned.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_unkown_event_type() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => 'SOME RANDOM EVENT'
]);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Override events should not have any limit restrictions returned.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_override_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$assign = $this->create_instance(['duedate' => $duedate]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
$record = (object) [
'assignid' => $instance->id,
'userid' => $userid
];
$DB->insert_record('assign_overrides', $record);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Assignments configured without a submissions from and cutoff date should not have
* any limits applied.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_no_limit() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => 0,
'cutoffdate' => 0,
]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Assignments should be bottom and top bound by the submissions from date and cutoff date
* respectively.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_with_limits() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertEquals($submissionsfromdate, $min[0]);
$this->assertNotEmpty($min[1]);
$this->assertEquals($cutoffdate, $max[0]);
$this->assertNotEmpty($max[1]);
}
/**
* Assignment grading due date should not have any limits of no due date and cutoff date is set.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_no_limit() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$assign = $this->create_instance([
'duedate' => 0,
'allowsubmissionsfromdate' => 0,
'cutoffdate' => 0,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE
]);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Assignment grading due event is minimum bound by the due date, if it is set.
*/
public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_with_due_date() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$assign = $this->create_instance([
'duedate' => $duedate
]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE
]);
list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event);
$this->assertEquals($duedate, $min[0]);
$this->assertNotEmpty($min[1]);
$this->assertNull($max);
}
/**
* Calendar events without and instance id should be ignored by the validate
* event function.
*/
public function test_mod_assign_core_calendar_validate_event_timestart_no_instance_id() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$event = new \calendar_event((object) [
'modulename' => 'assign',
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
mod_assign_core_calendar_validate_event_timestart($event);
// The function above throws an exception so all we need to do is make sure
// it gets here and that is considered success.
$this->assertTrue(true);
}
/**
* Calendar events for an unknown instance should throw an exception.
*/
public function test_mod_assign_core_calendar_validate_event_timestart_no_instance_found() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$event = new \calendar_event((object) [
'modulename' => 'assign',
'instance' => 1234,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
$this->expectException('moodle_exception');
mod_assign_core_calendar_validate_event_timestart($event);
}
/**
* Assignments configured without any limits on the due date should not
* throw an exception.
*/
public function test_mod_assign_core_calendar_validate_event_timestart_no_limit() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$assign = $this->create_instance([
'duedate' => 0,
'allowsubmissionsfromdate' => 0,
'cutoffdate' => 0,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => time()
]);
mod_assign_core_calendar_validate_event_timestart($event);
// The function above throws an exception so all we need to do is make sure
// it gets here and that is considered success.
$this->assertTrue(true);
}
/**
* Due date events with a timestart equal to or greater than the minimum limit
* should not throw an exception. Timestart values below the minimum limit should
* throw an exception.
*/
public function test_mod_assign_core_calendar_validate_due_event_min_limit() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => $submissionsfromdate + 1,
]);
// No exception when new time is above minimum cutoff.
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// No exception when new time is equal to minimum cutoff.
$event->timestart = $submissionsfromdate;
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// Exception when new time is earlier than minimum cutoff.
$event->timestart = $submissionsfromdate - 1;
$this->expectException('moodle_exception');
mod_assign_core_calendar_validate_event_timestart($event);
}
/**
* A due date event with a timestart less than or equal to the max limit should
* not throw an exception. A timestart greater than the max limit should throw
* an exception.
*/
public function test_mod_assign_core_calendar_validate_due_event_max_limit() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => $cutoffdate - 1,
]);
// No exception when new time is below maximum cutoff.
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// No exception when new time is equal to maximum cutoff.
$event->timestart = $cutoffdate;
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// Exception when new time is later than maximum cutoff.
$event->timestart = $submissionsfromdate - 1;
$this->expectException('moodle_exception');
mod_assign_core_calendar_validate_event_timestart($event);
}
/**
* Due date override events should not throw an exception.
*/
public function test_mod_assign_core_calendar_validate_due_event_override() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => $duedate + (2 * DAYSECS)
]);
$record = (object) [
'assignid' => $instance->id,
'userid' => $userid,
'duedate' => $duedate + (2 * DAYSECS)
];
$DB->insert_record('assign_overrides', $record);
// No exception when dealing with an override.
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
}
/**
* Grading due date event should throw an exception if it's timestart is less than the
* assignment due date.
*/
public function test_mod_assign_core_calendar_validate_gradingdue_event_min_limit_duedate() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE,
'timestart' => $duedate + 1,
]);
// No exception when new time is above minimum cutoff.
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// No exception when new time is equal to minimum cutoff.
$event->timestart = $duedate;
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// Exception when new time is earlier than minimum cutoff.
$event->timestart = $duedate - 1;
$this->expectException('moodle_exception');
mod_assign_core_calendar_validate_event_timestart($event);
}
/**
* Grading due date event should throw an exception if it's timestart is less than the
* submissions allowed from date if there is no due date set.
*/
public function test_mod_assign_core_calendar_validate_gradingdue_event_min_limit_submissionsfromdate() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = 0;
$submissionsfromdate = time() - DAYSECS;
$cutoffdate = time() + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE,
'timestart' => $submissionsfromdate + 1,
]);
// No exception when new time is above minimum cutoff.
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// No exception when new time is equal to minimum cutoff.
$event->timestart = $submissionsfromdate;
mod_assign_core_calendar_validate_event_timestart($event);
$this->assertTrue(true);
// Exception when new time is earlier than minimum cutoff.
$event->timestart = $submissionsfromdate - 1;
$this->expectException('moodle_exception');
mod_assign_core_calendar_validate_event_timestart($event);
}
/**
* Non due date events should not update the assignment due date.
*/
public function test_mod_assign_core_calendar_event_timestart_updated_non_due_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE,
'timestart' => $duedate + 1
]);
mod_assign_core_calendar_event_timestart_updated($event);
$newinstance = $DB->get_record('assign', ['id' => $instance->id]);
$this->assertEquals($duedate, $newinstance->duedate);
}
/**
* Due date override events should not change the assignment due date.
*/
public function test_mod_assign_core_calendar_event_timestart_updated_due_event_override() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$userid = $this->students[0]->id;
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'userid' => $userid,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => $duedate + 1
]);
$record = (object) [
'assignid' => $instance->id,
'userid' => $userid,
'duedate' => $duedate + 1
];
$DB->insert_record('assign_overrides', $record);
mod_assign_core_calendar_event_timestart_updated($event);
$newinstance = $DB->get_record('assign', ['id' => $instance->id]);
$this->assertEquals($duedate, $newinstance->duedate);
}
/**
* Due date events should update the assignment due date.
*/
public function test_mod_assign_core_calendar_event_timestart_updated_due_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$duedate = time();
$newduedate = $duedate + 1;
$submissionsfromdate = $duedate - DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$assign = $this->create_instance([
'duedate' => $duedate,
'allowsubmissionsfromdate' => $submissionsfromdate,
'cutoffdate' => $cutoffdate,
]);
$instance = $assign->get_instance();
$event = new \calendar_event((object) [
'courseid' => $instance->course,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE,
'timestart' => $newduedate
]);
mod_assign_core_calendar_event_timestart_updated($event);
$newinstance = $DB->get_record('assign', ['id' => $instance->id]);
$this->assertEquals($newduedate, $newinstance->duedate);
}
/**
* If a student somehow finds a way to update the due date calendar event
* then the callback should not be executed to update the assignment due
* date as well otherwise that would be a security issue.
*/
public function test_student_role_cant_update_due_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$mapper = calendar_event_container::get_event_mapper();
$generator = $this->getDataGenerator();
$user = $generator->create_user();
$course = $generator->create_course();
$context = context_course::instance($course->id);
$roleid = $generator->create_role();
$now = time();
$duedate = (new DateTime())->setTimestamp($now);
$newduedate = (new DateTime())->setTimestamp($now)->modify('+1 day');
$assign = $this->create_instance([
'course' => $course->id,
'duedate' => $duedate->getTimestamp(),
]);
$instance = $assign->get_instance();
$generator->enrol_user($user->id, $course->id, 'student');
$generator->role_assign($roleid, $user->id, $context->id);
$record = $DB->get_record('event', [
'courseid' => $course->id,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
$event = new \calendar_event($record);
assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleid, $context, true);
$this->setUser($user);
calendar_local_api::update_event_start_day(
$mapper->from_legacy_event_to_event($event),
$newduedate
);
$newinstance = $DB->get_record('assign', ['id' => $instance->id]);
$newevent = \calendar_event::load($event->id);
// The due date shouldn't have changed even though we updated the calendar
// event.
$this->assertEquals($duedate->getTimestamp(), $newinstance->duedate);
$this->assertEquals($newduedate->getTimestamp(), $newevent->timestart);
}
/**
* A teacher with the capability to modify an assignment module should be
* able to update the assignment due date by changing the due date calendar
* event.
*/
public function test_teacher_role_can_update_due_event() {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest();
$this->setAdminUser();
$mapper = calendar_event_container::get_event_mapper();
$generator = $this->getDataGenerator();
$user = $generator->create_user();
$course = $generator->create_course();
$context = context_course::instance($course->id);
$roleid = $generator->create_role();
$now = time();
$duedate = (new DateTime())->setTimestamp($now);
$newduedate = (new DateTime())->setTimestamp($now)->modify('+1 day');
$assign = $this->create_instance([
'course' => $course->id,
'duedate' => $duedate->getTimestamp(),
]);
$instance = $assign->get_instance();
$generator->enrol_user($user->id, $course->id, 'teacher');
$generator->role_assign($roleid, $user->id, $context->id);
$record = $DB->get_record('event', [
'courseid' => $course->id,
'modulename' => 'assign',
'instance' => $instance->id,
'eventtype' => ASSIGN_EVENT_TYPE_DUE
]);
$event = new \calendar_event($record);
assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleid, $context, true);
$this->setUser($user);
// Trigger and capture the event when adding a contact.
$sink = $this->redirectEvents();
calendar_local_api::update_event_start_day(
$mapper->from_legacy_event_to_event($event),
$newduedate
);
$triggeredevents = $sink->get_events();
$moduleupdatedevents = array_filter($triggeredevents, function($e) {
return is_a($e, 'core\event\course_module_updated');
});
$newinstance = $DB->get_record('assign', ['id' => $instance->id]);
$newevent = \calendar_event::load($event->id);
// The due date shouldn't have changed even though we updated the calendar
// event.
$this->assertEquals($newduedate->getTimestamp(), $newinstance->duedate);
$this->assertEquals($newduedate->getTimestamp(), $newevent->timestart);
// Confirm that a module updated event is fired when the module
// is changed.
$this->assertNotEmpty($moduleupdatedevents);
}
}