Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Unit tests for (some of) mod/assign/lib.php.
 *
 * @package    mod_assign
 * @category   phpunit
 * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
namespace mod_assign;

defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once($CFG->dirroot . '/mod/assign/lib.php');
require_once($CFG->dirroot . '/mod/assign/locallib.php');
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');

use core_calendar\local\api as calendar_local_api;
use core_calendar\local\event\container as calendar_event_container;
use mod_assign_test_generator;

/**
 * Unit tests for (some of) mod/assign/lib.php.
 *
 * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class lib_test extends \advanced_testcase {

    // Use the generator helper.
    use mod_assign_test_generator;

    /**
     * Test that assign_print_recent_activity shows ungraded submitted assignments.
     */
    public function test_print_recent_activity() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);
        $this->submit_for_grading($student, $assign);

        $this->setUser($teacher);
        $this->expectOutputRegex('/submitted:/');
        assign_print_recent_activity($course, true, time() - 3600);
    }

    /**
     * Test that assign_print_recent_activity does not display any warnings when a custom fullname has been configured.
     */
    public function test_print_recent_activity_fullname() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);
        $this->submit_for_grading($student, $assign);

        $this->setUser($teacher);
        $this->expectOutputRegex('/submitted:/');
        set_config('fullnamedisplay', 'firstname, lastnamephonetic');
        assign_print_recent_activity($course, false, time() - 3600);
    }

    /**
     * Test that assign_print_recent_activity shows the blind marking ID.
     */
    public function test_print_recent_activity_fullname_blind_marking() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');

        $assign = $this->create_instance($course, [
                'blindmarking' => 1,
            ]);
        $this->add_submission($student, $assign);
        $this->submit_for_grading($student, $assign);

        $this->setUser($teacher);
        $uniqueid = $assign->get_uniqueid_for_user($student->id);
        $expectedstr = preg_quote(get_string('participant', 'mod_assign'), '/') . '.*' . $uniqueid;
        $this->expectOutputRegex("/{$expectedstr}/");
        assign_print_recent_activity($course, false, time() - 3600);
    }

    /**
     * Test that assign_get_recent_mod_activity fetches the assignment correctly.
     */
    public function test_assign_get_recent_mod_activity() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);
        $this->add_submission($student, $assign);
        $this->submit_for_grading($student, $assign);

        $index = 1;
        $activities = [
            $index => (object) [
                'type' => 'assign',
                'cmid' => $assign->get_course_module()->id,
            ],
        ];

        $this->setUser($teacher);
        assign_get_recent_mod_activity($activities, $index, time() - HOURSECS, $course->id, $assign->get_course_module()->id);

        $activity = $activities[1];
        $this->assertEquals("assign", $activity->type);
        $this->assertEquals($student->id, $activity->user->id);
    }

    /**
     * Ensure that assign_user_complete displays information about drafts.
     */
    public function test_assign_user_complete() {
        global $PAGE, $DB;

        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
        $this->add_submission($student, $assign);

        $PAGE->set_url(new \moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));

        $submission = $assign->get_user_submission($student->id, true);
        $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
        $DB->update_record('assign_submission', $submission);

        $this->expectOutputRegex('/Draft/');
        assign_user_complete($course, $student, $assign->get_course_module(), $assign->get_instance());
    }

    /**
     * Ensure that assign_user_outline fetches updated grades.
     */
    public function test_assign_user_outline() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);

        $this->add_submission($student, $assign);
        $this->submit_for_grading($student, $assign);
        $this->mark_submission($teacher, $assign, $student, 50.0);

        $this->setUser($teacher);
        $data = $assign->get_user_grade($student->id, true);
        $data->grade = '50.5';
        $assign->update_grade($data);

        $result = assign_user_outline($course, $student, $assign->get_course_module(), $assign->get_instance());

        $this->assertMatchesRegularExpression('/50.5/', $result->info);
    }

    /**
     * Ensure that assign_get_completion_state reflects the correct status at each point.
     */
    public function test_assign_get_completion_state() {
        global $DB;

        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, [
                'submissiondrafts' => 0,
                'completionsubmit' => 1
            ]);

        $this->setUser($student);
        $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
        $this->assertFalse($result);

        $this->add_submission($student, $assign);
        $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
        $this->assertFalse($result);

        $this->submit_for_grading($student, $assign);
        $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
        $this->assertTrue($result);

        $this->mark_submission($teacher, $assign, $student, 50.0);
        $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
        $this->assertTrue($result);
    }

    /**
     * Tests for mod_assign_refresh_events.
     */
    public function test_assign_refresh_events() {
        global $DB;

        $this->resetAfterTest();

        $duedate = time();
        $newduedate = $duedate + DAYSECS;

        $this->setAdminUser();

        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, [
                'duedate' => $duedate,
            ]);

        $instance = $assign->get_instance();
        $eventparams = [
            'modulename' => 'assign',
            'instance' => $instance->id,
            'eventtype' => ASSIGN_EVENT_TYPE_DUE,
            'groupid' => 0
        ];

        // Make sure the calendar event for assignment 1 matches the initial due date.
        $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
        $this->assertEquals($eventtime, $duedate);

        // Manually update assignment 1's due date.
        $DB->update_record('assign', (object) [
            'id' => $instance->id,
            'duedate' => $newduedate,
            'course' => $course->id
        ]);

        // Then refresh the assignment events of assignment 1's course.
        $this->assertTrue(assign_refresh_events($course->id));

        // Confirm that the assignment 1's due date event now has the new due date after refresh.
        $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
        $this->assertEquals($eventtime, $newduedate);

        // Create a second course and assignment.
        $othercourse = $this->getDataGenerator()->create_course();;
        $otherassign = $this->create_instance($othercourse, [
            'duedate' => $duedate,
        ]);
        $otherinstance = $otherassign->get_instance();

        // Manually update assignment 1 and 2's due dates.
        $newduedate += DAYSECS;
        $DB->update_record('assign', (object)[
            'id' => $instance->id,
            'duedate' => $newduedate,
            'course' => $course->id
        ]);
        $DB->update_record('assign', (object)[
            'id' => $otherinstance->id,
            'duedate' => $newduedate,
            'course' => $othercourse->id
        ]);

        // Refresh events of all courses and check the calendar events matches the new date.
        $this->assertTrue(assign_refresh_events());

        // Check the due date calendar event for assignment 1.
        $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
        $this->assertEquals($eventtime, $newduedate);

        // Check the due date calendar event for assignment 2.
        $eventparams['instance'] = $otherinstance->id;
        $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
        $this->assertEquals($eventtime, $newduedate);

        // In case the course ID is passed as a numeric string.
        $this->assertTrue(assign_refresh_events('' . $course->id));

        // Non-existing course ID.
        $this->assertFalse(assign_refresh_events(-1));

        // Invalid course ID.
        $this->assertFalse(assign_refresh_events('aaa'));
    }

    public function test_assign_core_calendar_is_event_visible_duedate_event_as_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        $this->setAdminUser();

        // Create a calendar event.
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // The teacher should see the due date event.
        $this->setUser($teacher);
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
    }

    public function test_assign_core_calendar_is_event_visible_duedate_event_for_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        $this->setAdminUser();

        // Create a calendar event.
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // Now, log out.
        $this->setUser();

        // The teacher should see the due date event.
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
    }

    public function test_assign_core_calendar_is_event_visible_duedate_event_as_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);

        $this->setAdminUser();

        // Create a calendar event.
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // The student should care about the due date event.
        $this->setUser($student);
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
    }

    public function test_assign_core_calendar_is_event_visible_duedate_event_for_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);

        $this->setAdminUser();

        // Create a calendar event.
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // Now, log out.
        $this->setUser();

        // The student should care about the due date event.
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $student->id));
    }

    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);

        // The teacher should see the due date event.
        $this->setUser($teacher);
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
    }


    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);

        // Now, log out.
        $this->setUser();

        // The teacher should see the due date event.
        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
    }

    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);

        // The student should not see the due date event.
        $this->setUser($student);
        $this->assertFalse(mod_assign_core_calendar_is_event_visible($event));
    }


    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);

        // Now, log out.
        $this->setUser();

        // The student should not see the due date event.
        $this->assertFalse(mod_assign_core_calendar_is_event_visible($event, $student->id));
    }

    public function test_assign_core_calendar_provide_event_action_duedate_as_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // The teacher should see the event.
        $this->setUser($teacher);
        $factory = new \core_calendar\action_factory();
        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);

        // The teacher should not have an action for a due date event.
        $this->assertNull($actionevent);
    }

    public function test_assign_core_calendar_provide_event_action_duedate_for_teacher() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $assign = $this->create_instance($course);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // Now, log out.
        $this->setUser();

        // Decorate action event for a teacher.
        $factory = new \core_calendar\action_factory();
        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id);

        // The teacher should not have an action for a due date event.
        $this->assertNull($actionevent);
    }

    public function test_assign_core_calendar_provide_event_action_duedate_as_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // The student should see the event.
        $this->setUser($student);
        $factory = new \core_calendar\action_factory();
        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);

        // Confirm the event was decorated.
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
        $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name());
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
        $this->assertEquals(1, $actionevent->get_item_count());
        $this->assertTrue($actionevent->is_actionable());
    }

    public function test_assign_core_calendar_provide_event_action_duedate_for_student() {
        $this->resetAfterTest();
        $course = $this->getDataGenerator()->create_course();
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);

        // Create a calendar event.
        $this->setAdminUser();
        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);

        // Now, log out.
        $this->setUser();

        // Decorate action event for a student.
        $factory = new \core_calendar\action_factory();
        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);

        // Confirm the event was decorated.
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
        $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name());
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
        $this->assertEquals(1, $actionevent->get_item_count());
        $this->assertTrue($actionevent->is_actionable());
    }

> /** public function test_assign_core_calendar_provide_event_action_gradingduedate_as_teacher() { > * Test group submissions. $this->resetAfterTest(); > * @covers \assign::mod_assign_core_calendar_provide_event_action $course = $this->getDataGenerator()->create_course(); > */ $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); > public function test_assign_core_calendar_provide_event_action_duedate_for_group_assignment(): void { $assign = $this->create_instance($course); > $this->resetAfterTest(); > $course = $this->getDataGenerator()->create_course(); // Create a calendar event. > $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $this->setAdminUser(); > $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); > $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); > $group = $this->getDataGenerator()->create_group([ $this->setUser($teacher); > 'courseid' => $course->id, $factory = new \core_calendar\action_factory(); > ]); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); > groups_add_member($group->id, $student1->id); > groups_add_member($group->id, $student2->id); // Confirm the event was decorated. > $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); > $assign = $this->create_instance($course, [ $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); > 'assignsubmission_onlinetext_enabled' => 1, $this->assertInstanceOf('moodle_url', $actionevent->get_url()); > 'requiresubmissionstatement' => 1, $this->assertEquals(0, $actionevent->get_item_count()); > 'teamsubmission' => 1, $this->assertTrue($actionevent->is_actionable()); > 'completion' => 0, } > 'completionsubmit' => 1, > ]); public function test_assign_core_calendar_provide_event_action_gradingduedate_for_teacher() { > $this->resetAfterTest(); > // Create a calendar event. $course = $this->getDataGenerator()->create_course(); > $this->setAdminUser(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); > $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); $assign = $this->create_instance($course); > $this->setUser(); > // Create a calendar event. > $factory = new \core_calendar\action_factory(); $this->setAdminUser(); > $actionevent1 = mod_assign_core_calendar_provide_event_action($event, $factory, $student1->id); $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); > $actionevent2 = mod_assign_core_calendar_provide_event_action($event, $factory, $student2->id); > // Now, log out. > // Confirm the events were decorated. $this->setUser(); > $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent1); > $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent1->get_name()); // Decorate action event for a teacher. > $this->assertInstanceOf('moodle_url', $actionevent1->get_url()); $factory = new \core_calendar\action_factory(); > $this->assertEquals(1, $actionevent1->get_item_count()); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id); > $this->assertTrue($actionevent1->is_actionable()); > // Confirm the event was decorated. > $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent2); $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); > $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent2->get_name()); $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); > $this->assertInstanceOf('moodle_url', $actionevent2->get_url()); $this->assertInstanceOf('moodle_url', $actionevent->get_url()); > $this->assertEquals(1, $actionevent2->get_item_count()); $this->assertEquals(0, $actionevent->get_item_count()); > $this->assertTrue($actionevent2->is_actionable()); $this->assertTrue($actionevent->is_actionable()); > } > // Submit as the student. > $this->add_submission($student1, $assign); public function test_assign_core_calendar_provide_event_action_gradingduedate_as_student() { > $this->submit_for_grading($student1, $assign, ['submissionstatement' => 'Hello, world!']); $this->resetAfterTest(); > $course = $this->getDataGenerator()->create_course(); > // Create a new calendar event. $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); > $this->setAdminUser(); $assign = $this->create_instance($course); > $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); > $this->setUser(); // Create a calendar event. > $this->setAdminUser(); > // Confirm there were no events to action. $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); > $actionevent1 = mod_assign_core_calendar_provide_event_action($event, $factory, $student1->id); > $this->assertNull($actionevent1); $this->setUser($student); > $factory = new \core_calendar\action_factory(); > $actionevent2 = mod_assign_core_calendar_provide_event_action($event, $factory, $student2->id); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); > $this->assertNull($actionevent2); > } // Confirm the event was decorated. >
$this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); $this->assertInstanceOf('moodle_url', $actionevent->get_url()); $this->assertEquals(0, $actionevent->get_item_count()); $this->assertFalse($actionevent->is_actionable()); } public function test_assign_core_calendar_provide_event_action_gradingduedate_for_student() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course); // Create a calendar event. $this->setAdminUser(); $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); // Now, log out. $this->setUser(); // Decorate action event for a student. $factory = new \core_calendar\action_factory(); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); // Confirm the event was decorated. $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); $this->assertInstanceOf('moodle_url', $actionevent->get_url()); $this->assertEquals(0, $actionevent->get_item_count()); $this->assertFalse($actionevent->is_actionable()); } public function test_assign_core_calendar_provide_event_action_duedate_as_student_submitted() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); $this->setAdminUser(); // Create a calendar event. $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); // Create an action factory. $factory = new \core_calendar\action_factory(); // Submit as the student. $this->add_submission($student, $assign); $this->submit_for_grading($student, $assign); // Confirm there was no event to action. $factory = new \core_calendar\action_factory(); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); $this->assertNull($actionevent); } public function test_assign_core_calendar_provide_event_action_duedate_for_student_submitted() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); $this->setAdminUser(); // Create a calendar event. $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); // Create an action factory. $factory = new \core_calendar\action_factory(); // Submit as the student. $this->add_submission($student, $assign); $this->submit_for_grading($student, $assign); // Now, log out. $this->setUser(); // Confirm there was no event to action. $factory = new \core_calendar\action_factory(); $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); $this->assertNull($actionevent); } public function test_assign_core_calendar_provide_event_action_already_completed() { $this->resetAfterTest(); set_config('enablecompletion', 1); $this->setAdminUser(); // Create the activity. $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); $assign = $this->create_instance($course, ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]); // Get some additional data. $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id); // Create a calendar event. $event = $this->create_action_event($course, $assign, \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); // Mark the activity as completed. $completion = new \completion_info($course); $completion->set_module_viewed($cm); // Create an action factory. $factory = new \core_calendar\action_factory(); // Decorate action event. $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); // Ensure result was null. $this->assertNull($actionevent); } public function test_assign_core_calendar_provide_event_action_already_completed_for_user() { $this->resetAfterTest(); set_config('enablecompletion', 1); $this->setAdminUser(); // Create the activity. $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); $assign = $this->create_instance($course, ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]); // Enrol a student in the course. $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); // Get some additional data. $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id); // Create a calendar event. $event = $this->create_action_event($course, $assign, \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); // Mark the activity as completed for the student. $completion = new \completion_info($course); $completion->set_module_viewed($cm, $student->id); // Create an action factory. $factory = new \core_calendar\action_factory(); // Decorate action event for the student. $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); // Ensure result was null. $this->assertNull($actionevent); } /** * Creates an action event. * * @param \stdClass $course The course the assignment is in * @param assign $assign The assignment to create an event for * @param string $eventtype The event type. eg. ASSIGN_EVENT_TYPE_DUE. * @return bool|calendar_event */ private function create_action_event($course, $assign, $eventtype) { $event = new \stdClass(); $event->name = 'Calendar event'; $event->modulename = 'assign'; $event->courseid = $course->id; $event->instance = $assign->get_instance()->id; $event->type = CALENDAR_EVENT_TYPE_ACTION; $event->eventtype = $eventtype; $event->timestart = time(); return \calendar_event::create($event); } /** * Test the callback responsible for returning the completion rule descriptions. * This function should work given either an instance of the module (cm_info), such as when checking the active rules, * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. */ public function test_mod_assign_completion_get_active_rule_descriptions() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); $this->setAdminUser(); // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. $cm1 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '1'])->get_course_module(); $cm2 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '0'])->get_course_module(); // Data for the stdClass input type. // This type of input would occur when checking the default completion rules for an activity type, where we don't have // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. $moddefaults = (object) [ 'customdata' => [ 'customcompletionrules' => [ 'completionsubmit' => '1', ], ], 'completion' => 2, ]; $activeruledescriptions = [get_string('completionsubmit', 'assign')]; $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm2), []); $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); $this->assertEquals(mod_assign_get_completion_active_rule_descriptions(new \stdClass()), []); } /** * Test that if some grades are not set, they are left alone and not rescaled */ public function test_assign_rescale_activity_grades_some_unset() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student'); // As a teacher. $this->setUser($teacher); $assign = $this->create_instance($course); // Grade the student. $data = ['grade' => 50]; $assign->testable_apply_grade_to_user((object)$data, $student->id, 0); // Try getting another students grade. This will give a grade of ASSIGN_GRADE_NOT_SET (-1). $assign->get_user_grade($otherstudent->id, true); // Rescale. assign_rescale_activity_grades($course, $assign->get_course_module(), 0, 100, 0, 10); // Get the grades for both students. $studentgrade = $assign->get_user_grade($student->id, true); $otherstudentgrade = $assign->get_user_grade($otherstudent->id, true); // Make sure the real grade is scaled, but the ASSIGN_GRADE_NOT_SET stays the same. $this->assertEquals($studentgrade->grade, 5); $this->assertEquals($otherstudentgrade->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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $instance = $assign->get_instance(); $event = new \calendar_event((object)[ 'modulename' => 'assign', 'instance' => $instance->id, 'userid' => $student->id, ]); $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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $userid = $student->id; $duedate = time(); $assign = $this->create_instance($course, ['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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $instance = $assign->get_instance(); $otherassign = $this->create_instance($course, ['duedate' => $duedate]); $otherinstance = $otherassign->get_instance(); $event = new \calendar_event((object) [ 'modulename' => 'assign', 'instance' => $instance->id, 'userid' => $student->id, ]); $DB->insert_record('assign_overrides', (object) [ 'assignid' => $otherinstance->id, 'userid' => $student->id, ]); $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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $instance = $assign->get_instance(); $event = new \calendar_event((object) [ 'modulename' => 'assign', 'instance' => $instance->id, 'userid' => $student->id, ]); $DB->insert_record('assign_overrides', (object) [ 'assignid' => $instance->id, 'userid' => $student->id, ]); $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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $instance = $assign->get_instance(); $group = $this->getDataGenerator()->create_group(array('courseid' => $instance->course)); $event = new \calendar_event((object) [ 'modulename' => 'assign', 'instance' => $instance->id, 'groupid' => $group->id, ]); $DB->insert_record('assign_overrides', (object) [ 'assignid' => $instance->id, 'groupid' => $group->id, ]); $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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['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, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $instance = $assign->get_instance(); $event = new \calendar_event((object) [ 'courseid' => $instance->course, 'modulename' => 'assign', 'instance' => $instance->id, 'userid' => $student->id, 'eventtype' => ASSIGN_EVENT_TYPE_DUE ]); $record = (object) [ 'assignid' => $instance->id, 'userid' => $student->id, ]; $DB->insert_record('assign_overrides', $record); list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); $this->assertFalse($min); $this->assertFalse($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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, [ 'duedate' => $duedate, '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 ]); list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $duedate = time(); $submissionsfromdate = $duedate - DAYSECS; $cutoffdate = $duedate + DAYSECS; $assign = $this->create_instance($course, [ '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 ]); list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $assign = $this->create_instance($course, [ '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, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); $duedate = time(); $assign = $this->create_instance($course, ['duedate' => $duedate]); $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, $instance); $this->assertEquals($duedate, $min[0]); $this->assertNotEmpty($min[1]); $this->assertNull($max); } /** * 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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $submissionsfromdate = $duedate - DAYSECS; $cutoffdate = $duedate + DAYSECS; $assign = $this->create_instance($course, [ '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, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $submissionsfromdate = $duedate - DAYSECS; $cutoffdate = $duedate + DAYSECS; $assign = $this->create_instance($course, [ 'duedate' => $duedate, 'allowsubmissionsfromdate' => $submissionsfromdate, 'cutoffdate' => $cutoffdate, ]); $instance = $assign->get_instance(); $event = new \calendar_event((object) [ 'courseid' => $instance->course, 'modulename' => 'assign', 'instance' => $instance->id, 'userid' => $student->id, 'eventtype' => ASSIGN_EVENT_TYPE_DUE, 'timestart' => $duedate + 1 ]); $record = (object) [ 'assignid' => $instance->id, 'userid' => $student->id, 'duedate' => $duedate + 1, ]; $DB->insert_record('assign_overrides', $record); mod_assign_core_calendar_event_timestart_updated($event, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $this->setAdminUser(); $duedate = time(); $newduedate = $duedate + 1; $submissionsfromdate = $duedate - DAYSECS; $cutoffdate = $duedate + DAYSECS; $assign = $this->create_instance($course, [ '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, $instance); $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(); $course = $this->getDataGenerator()->create_course(); $context = \context_course::instance($course->id); $roleid = $this->getDataGenerator()->create_role(); $role = $DB->get_record('role', ['id' => $roleid]); $user = $this->getDataGenerator()->create_and_enrol($course, $role->shortname); $this->setAdminUser(); $mapper = calendar_event_container::get_event_mapper(); $now = time(); $duedate = (new \DateTime())->setTimestamp($now); $newduedate = (new \DateTime())->setTimestamp($now)->modify('+1 day'); $assign = $this->create_instance($course, [ 'course' => $course->id, 'duedate' => $duedate->getTimestamp(), ]); $instance = $assign->get_instance(); $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(); $course = $this->getDataGenerator()->create_course(); $context = \context_course::instance($course->id); $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $roleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']); $this->setAdminUser(); $mapper = calendar_event_container::get_event_mapper(); $now = time(); $duedate = (new \DateTime())->setTimestamp($now); $newduedate = (new \DateTime())->setTimestamp($now)->modify('+1 day'); $assign = $this->create_instance($course, [ 'course' => $course->id, 'duedate' => $duedate->getTimestamp(), ]); $instance = $assign->get_instance(); $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); } /** * A user who does not have capabilities to add events to the calendar should be able to create an assignment. */ public function test_creation_with_no_calendar_capabilities() { $this->resetAfterTest(); $course = self::getDataGenerator()->create_course(); $context = \context_course::instance($course->id); $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); $roleid = self::getDataGenerator()->create_role(); self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); $generator = self::getDataGenerator()->get_plugin_generator('mod_assign'); // Create an instance as a user without the calendar capabilities. $this->setUser($user); $time = time(); $params = array( 'course' => $course->id, 'allowsubmissionsfromdate' => $time, 'duedate' => $time + 500, 'cutoffdate' => $time + 600, 'gradingduedate' => $time + 700, ); $generator->create_instance($params); } }