diff --git a/enrol/lti/db/upgrade.php b/enrol/lti/db/upgrade.php index 3b852efc25f41..9a60ac89b378a 100644 --- a/enrol/lti/db/upgrade.php +++ b/enrol/lti/db/upgrade.php @@ -459,5 +459,17 @@ function xmldb_enrol_lti_upgrade($oldversion) { // Automatically generated Moodle v4.0.0 release upgrade line. // Put any upgrade step following this. + if ($oldversion < 2022041901) { + // Disable all orphaned enrolment method instances. + $sql = "id IN (SELECT t.enrolid + FROM {enrol_lti_tools} t + LEFT JOIN {context} c ON (t.contextid = c.id) + WHERE c.id IS NULL)"; + $DB->set_field_select('enrol', 'status', 1, $sql); + + // Lti savepoint reached. + upgrade_plugin_savepoint(true, 2022041901, 'enrol', 'lti'); + } + return true; } diff --git a/enrol/lti/lib.php b/enrol/lti/lib.php index 684993a20bfe5..f01522cb9e129 100644 --- a/enrol/lti/lib.php +++ b/enrol/lti/lib.php @@ -479,3 +479,18 @@ function enrol_lti_get_fontawesome_icon_map() { 'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger', ]; } + +/** + * Pre-delete course module hook which disables any methods referring to the deleted module, preventing launches and allowing remap. + * + * @param stdClass $cm The deleted course module record. + */ +function enrol_lti_pre_course_module_delete(stdClass $cm) { + global $DB; + $sql = "id IN (SELECT t.enrolid + FROM {enrol_lti_tools} t + JOIN {context} c ON (t.contextid = c.id) + WHERE c.contextlevel = :contextlevel + AND c.instanceid = :cmid)"; + $DB->set_field_select('enrol', 'status', ENROL_INSTANCE_DISABLED, $sql, ['contextlevel' => CONTEXT_MODULE, 'cmid' => $cm->id]); +} diff --git a/enrol/lti/tests/lib_test.php b/enrol/lti/tests/lib_test.php index d11e0f826990e..9698d5e818b49 100644 --- a/enrol/lti/tests/lib_test.php +++ b/enrol/lti/tests/lib_test.php @@ -216,4 +216,36 @@ public function test_get_user_enrolment_actions() { // LTI enrolment has 1 enrol actions for active users -- unenrol. $this->assertCount(1, $actions); } + + /** + * Test the behaviour of an enrolment method when the activity to which it provides access is deleted. + * + * @covers \enrol_lti_pre_course_module_delete + */ + public function test_course_module_deletion() { + // Create two modules and publish them. + $course = $this->getDataGenerator()->create_course(); + $mod = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); + $mod2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); + $tooldata = [ + 'cmid' => $mod->cmid, + 'courseid' => $course->id, + ]; + $tool = $this->getDataGenerator()->create_lti_tool((object)$tooldata); + $tooldata['cmid'] = $mod2->cmid; + $tool2 = $this->getDataGenerator()->create_lti_tool((object)$tooldata); + + // Verify the instances are both enabled. + $modinstance = helper::get_lti_tool($tool->id); + $mod2instance = helper::get_lti_tool($tool2->id); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $modinstance->status); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status); + + // Delete a module and verify the associated instance is disabled. + course_delete_module($mod->cmid); + $modinstance = helper::get_lti_tool($tool->id); + $mod2instance = helper::get_lti_tool($tool2->id); + $this->assertEquals(ENROL_INSTANCE_DISABLED, $modinstance->status); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status); + } } diff --git a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php index e8680a5f238bd..76cabc0184d38 100644 --- a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php +++ b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php @@ -419,6 +419,8 @@ public function test_sync_grades_no_service_endpoint() { */ public function test_sync_grades_deleted_context() { $this->resetAfterTest(); + global $DB; + [$course, $resource] = $this->create_test_environment(); $launchservice = $this->get_tool_launch_service(); @@ -427,10 +429,11 @@ public function test_sync_grades_deleted_context() { $instructoruser = $this->getDataGenerator()->create_user(); [$userid] = $launchservice->user_launches_tool($instructoruser, $teachermocklaunch); - global $CFG; - require_once($CFG->dirroot . '/course/lib.php'); + // Delete the activity, then enable the enrolment method (it is disabled during activity deletion). $modcontext = \context::instance_by_id($resource->contextid); course_delete_module($modcontext->instanceid); + $enrol = ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_ENABLED]; + $DB->update_record('enrol', $enrol); $task = $this->get_task_with_mocked_grade_service(); $this->expectOutputRegex( diff --git a/enrol/lti/version.php b/enrol/lti/version.php index 87b900db4a1a5..f51f117a91127 100644 --- a/enrol/lti/version.php +++ b/enrol/lti/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022041900; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2022041901; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022041200; // Requires this Moodle version. $plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics). $plugin->dependencies = [