From 1e96c0ba35bd35441b730abdbf4e0e2db1e2dcb3 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Tue, 14 Oct 2025 17:42:35 +0200 Subject: [PATCH] MDL-86856 course: Deprecate course_delete_module --- .upgradenotes/MDL-86856-2025101408325140.yml | 9 + public/course/lib.php | 223 +++--------------- .../tests/course_delete_modules_test.php | 2 +- 3 files changed, 37 insertions(+), 197 deletions(-) create mode 100644 .upgradenotes/MDL-86856-2025101408325140.yml diff --git a/.upgradenotes/MDL-86856-2025101408325140.yml b/.upgradenotes/MDL-86856-2025101408325140.yml new file mode 100644 index 00000000000..9adf3d6ad3a --- /dev/null +++ b/.upgradenotes/MDL-86856-2025101408325140.yml @@ -0,0 +1,9 @@ +issueNumber: MDL-86856 +notes: + core_course: + - message: >- + The following methods have been deprecated and should no longer be used: + - `course_delete_module` + - `course_module_flag_for_async_deletion` + Please consider using the equivalent methods, delete and delete_async, in `core_courseformat\local\cmactions` instead. + type: deprecated diff --git a/public/course/lib.php b/public/course/lib.php index e2b60b6295b..d0cfc43fb14 100644 --- a/public/course/lib.php +++ b/public/course/lib.php @@ -700,153 +700,20 @@ function set_coursemodule_name($cmid, $name) { * @param bool $async whether or not to try to delete the module using an adhoc task. Async also depends on a plugin hook. * @throws moodle_exception * @since Moodle 2.5 + * @deprecated since Moodle 5.2. + * @todo MDL-86956 Final deprecation in Moodle 6.0. */ +#[\core\attribute\deprecated( + replacement: 'core_courseformat\local\cmactions::delete', + since: '5.2', + mdl: 'MDL-86856', + reason: 'Course activity editing global functions have been moved to format actions', +)] function course_delete_module($cmid, $async = false) { - // Check the 'course_module_background_deletion_recommended' hook first. - // Only use asynchronous deletion if at least one plugin returns true and if async deletion has been requested. - // Both are checked because plugins should not be allowed to dictate the deletion behaviour, only support/decline it. - // It's up to plugins to handle things like whether or not they are enabled. - if ($async && $pluginsfunction = get_plugins_with_function('course_module_background_deletion_recommended')) { - foreach ($pluginsfunction as $plugintype => $plugins) { - foreach ($plugins as $pluginfunction) { - if ($pluginfunction()) { - return course_module_flag_for_async_deletion($cmid); - } - } - } - } + \core\deprecation::emit_deprecation(__FUNCTION__); - global $CFG, $DB; - - require_once($CFG->libdir.'/gradelib.php'); - require_once($CFG->libdir.'/questionlib.php'); - require_once($CFG->dirroot.'/blog/lib.php'); - require_once($CFG->dirroot.'/calendar/lib.php'); - - // Get the course module. - if (!$cm = $DB->get_record('course_modules', array('id' => $cmid))) { - return true; - } - - // Get the module context. - $modcontext = context_module::instance($cm->id); - - // Get the course module name. - $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module), MUST_EXIST); - - // Get the file location of the delete_instance function for this module. - $modlib = "$CFG->dirroot/mod/$modulename/lib.php"; - - // Include the file required to call the delete_instance function for this module. - if (file_exists($modlib)) { - require_once($modlib); - } else { - throw new moodle_exception('cannotdeletemodulemissinglib', '', '', null, - "Cannot delete this module as the file mod/$modulename/lib.php is missing."); - } - - // Warning! there is very similar code in remove_course_contents. - // If you are changing this code, you probably need to change that too. - $deleteinstancefunction = $modulename . '_delete_instance'; - - // Ensure the delete_instance function exists for this module. - if (!function_exists($deleteinstancefunction)) { - throw new moodle_exception('cannotdeletemodulemissingfunc', '', '', null, - "Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/$modulename/lib.php."); - } - - // Allow plugins to use this course module before we completely delete it. - if ($pluginsfunction = get_plugins_with_function('pre_course_module_delete')) { - foreach ($pluginsfunction as $plugintype => $plugins) { - foreach ($plugins as $pluginfunction) { - $pluginfunction($cm); - } - } - } - - if (empty($cm->instance)) { - throw new moodle_exception('cannotdeletemodulemissinginstance', '', '', null, - "Cannot delete course module with ID $cm->id because it does not have a valid activity instance."); - } - - // Call the delete_instance function, if it returns false throw an exception. - if (!$deleteinstancefunction($cm->instance)) { - throw new moodle_exception('cannotdeletemoduleinstance', '', '', null, - "Cannot delete the module $modulename (instance)."); - } - - // We delete the questions after the activity database is removed, - // because questions are referenced via question reference tables - // and cannot be deleted while the activities that use them still exist. - question_delete_activity($cm); - - // Remove all module files in case modules forget to do that. - $fs = get_file_storage(); - $fs->delete_area_files($modcontext->id); - - // Delete events from calendar. - if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename))) { - $coursecontext = context_course::instance($cm->course); - foreach($events as $event) { - $event->context = $coursecontext; - $calendarevent = calendar_event::load($event); - $calendarevent->delete(); - } - } - - // Delete grade items, outcome items and grades attached to modules. - if ($grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $modulename, - 'iteminstance' => $cm->instance, 'courseid' => $cm->course))) { - foreach ($grade_items as $grade_item) { - $grade_item->delete('moddelete'); - } - } - - // Delete associated blogs and blog tag instances. - blog_remove_associations_for_module($modcontext->id); - - // Delete completion and availability data; it is better to do this even if the - // features are not turned on, in case they were turned on previously (these will be - // very quick on an empty table). - $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id)); - $DB->delete_records('course_modules_viewed', ['coursemoduleid' => $cm->id]); - $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id, - 'course' => $cm->course, - 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY)); - - // Delete all tag instances associated with the instance of this module. - core_tag_tag::delete_instances('mod_' . $modulename, null, $modcontext->id); - core_tag_tag::remove_all_item_tags('core', 'course_modules', $cm->id); - - // Notify the competency subsystem. - \core_competency\api::hook_course_module_deleted($cm); - - // Delete the context. - context_helper::delete_instance(CONTEXT_MODULE, $cm->id); - - // Delete the module from the course_modules table. - $DB->delete_records('course_modules', array('id' => $cm->id)); - - // Delete module from that section. - if (!delete_mod_from_section($cm->id, $cm->section)) { - throw new moodle_exception('cannotdeletemodulefromsection', '', '', null, - "Cannot delete the module $modulename (instance) from section."); - } - - // Trigger event for course module delete action. - $event = \core\event\course_module_deleted::create(array( - 'courseid' => $cm->course, - 'context' => $modcontext, - 'objectid' => $cm->id, - 'other' => array( - 'modulename' => $modulename, - 'instanceid' => $cm->instance, - ) - )); - $event->add_record_snapshot('course_modules', $cm); - $event->trigger(); - \course_modinfo::purge_course_module_cache($cm->course, $cm->id); - rebuild_course_cache($cm->course, false, true); + $coursecontext = context_module::instance($cmid)->get_course_context(); + formatactions::cm($coursecontext->instanceid)->delete($cmid, $async); } /** @@ -858,61 +725,25 @@ function course_delete_module($cmid, $async = false) { * @param int $cmid the course module id. * @return ?bool whether the module was successfully scheduled for deletion. * @throws \moodle_exception + * @deprecated since Moodle 5.2. + * @todo MDL-86956 Final deprecation in Moodle 6.0. */ +#[\core\attribute\deprecated( + replacement: 'core_courseformat\local\cmactions::delete_async', + since: '5.2', + mdl: 'MDL-86856', + reason: 'Course activity editing global functions have been moved to format actions', +)] function course_module_flag_for_async_deletion($cmid) { - global $CFG, $DB, $USER; - require_once($CFG->libdir.'/gradelib.php'); - require_once($CFG->libdir.'/questionlib.php'); - require_once($CFG->dirroot.'/blog/lib.php'); - require_once($CFG->dirroot.'/calendar/lib.php'); + \core\deprecation::emit_deprecation(__FUNCTION__); - // Get the course module. - if (!$cm = $DB->get_record('course_modules', array('id' => $cmid))) { - return true; - } - - // We need to be reasonably certain the deletion is going to succeed before we background the process. - // Make the necessary delete_instance checks, etc. before proceeding further. Throw exceptions if required. - - // Get the course module name. - $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module), MUST_EXIST); - - // Get the file location of the delete_instance function for this module. - $modlib = "$CFG->dirroot/mod/$modulename/lib.php"; - - // Include the file required to call the delete_instance function for this module. - if (file_exists($modlib)) { - require_once($modlib); - } else { - throw new \moodle_exception('cannotdeletemodulemissinglib', '', '', null, - "Cannot delete this module as the file mod/$modulename/lib.php is missing."); - } - - $deleteinstancefunction = $modulename . '_delete_instance'; - - // Ensure the delete_instance function exists for this module. - if (!function_exists($deleteinstancefunction)) { - throw new \moodle_exception('cannotdeletemodulemissingfunc', '', '', null, - "Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/$modulename/lib.php."); - } - - // We are going to defer the deletion as we can't be sure how long the module's pre_delete code will run for. - $cm->deletioninprogress = '1'; - $DB->update_record('course_modules', $cm); - - // Create an adhoc task for the deletion of the course module. The task takes an array of course modules for removal. - $removaltask = new \core_course\task\course_delete_modules(); - $removaltask->set_custom_data(array( - 'cms' => array($cm), - 'userid' => $USER->id, - 'realuserid' => \core\session\manager::get_realuser()->id - )); - - // Queue the task for the next run. - \core\task\manager::queue_adhoc_task($removaltask); - - // Reset the course cache to hide the module. - rebuild_course_cache($cm->course, true); + $coursecontext = context_module::instance($cmid)->get_course_context(); + // The new method is correctly declared as protected to prevent direct use, a visibility modifier that was not possible + // in its original location. + // To avoid code duplication, as a temporary solution, because this method will be removed in the near future, + // it now calls the new protected method using reflection. + $method = new ReflectionMethod(\core_courseformat\local\cmactions::class, 'delete_async'); + $method->invokeArgs(formatactions::cm($coursecontext->instanceid), [$cmid]); } /** diff --git a/public/course/tests/course_delete_modules_test.php b/public/course/tests/course_delete_modules_test.php index 7e9f5d0033e..cc402b7d9f8 100644 --- a/public/course/tests/course_delete_modules_test.php +++ b/public/course/tests/course_delete_modules_test.php @@ -109,7 +109,7 @@ final class course_delete_modules_test extends \advanced_testcase { $errormsg = str_replace('\\', '/', $e->getMessage()); // Normalise dir separator. $this->assertStringContainsString('cannotdeletemodulemissinglib', $errormsg); $this->assertStringContainsString('course/lib.php', $errormsg); - $this->assertStringContainsString('mod/TestModuleToDelete/lib.php is missing', $errormsg); + $this->assertStringContainsString('Missing file mod/TestModuleToDelete/lib.php', $errormsg); // Get line numbers array which contains the exception name. $lines = array_keys(preg_grep("/cannotdeletemodulemissinglib/", file(dirname(__DIR__) . '/lib.php'))); // Increase 1 to keys to convert to actual line number.