From bfe14e23120989dfb84002c951e7aa571cd9db6c Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 1 Mar 2022 11:11:57 +0700 Subject: [PATCH] MDL-72991 Course: Partial course cache rebuild with cache versioning --- course/format/classes/base.php | 2 +- course/lib.php | 198 --------------------------- course/tests/courselib_test.php | 4 +- course/upgrade.txt | 2 + lib/deprecatedlib.php | 15 ++ lib/modinfolib.php | 233 +++++++++++++++++++++++++++++--- lib/moodlelib.php | 3 +- lib/tests/modinfolib_test.php | 10 +- mod/page/lib.php | 2 +- mod/resource/lib.php | 2 +- mod/url/lib.php | 2 +- 11 files changed, 243 insertions(+), 230 deletions(-) diff --git a/course/format/classes/base.php b/course/format/classes/base.php index cc83f570698..c75c95946e2 100644 --- a/course/format/classes/base.php +++ b/course/format/classes/base.php @@ -1393,7 +1393,7 @@ abstract class base { } if (!is_object($section)) { $section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section), - 'id,course,section,sequence,summary'); + 'id,section,sequence,summary'); } if (!$section || !$section->section) { // Not possible to delete 0-section. diff --git a/course/lib.php b/course/lib.php index dee093bbf48..e76d9145cc8 100644 --- a/course/lib.php +++ b/course/lib.php @@ -386,204 +386,6 @@ function course_integrity_check($courseid, $rawmods = null, $sections = null, $f return $messages; } -/** - * For a given course, returns an array of course activity objects - * Each item in the array contains he following properties: - * - * @param int $courseid course id - * @param bool $usecache get activities from cache if modinfo exists when $usecache is true - * @return array list of activities - */ -function get_array_of_activities(int $courseid, bool $usecache = false): array { -// cm - course module id -// mod - name of the module (eg forum) -// section - the number of the section (eg week or topic) -// name - the name of the instance -// visible - is the instance visible or not -// groupingid - grouping id -// extra - contains extra string to include in any link - global $CFG, $DB; - - $course = $DB->get_record('course', array('id'=>$courseid)); - - if (empty($course)) { - throw new moodle_exception('courseidnotfound'); - } - - $rawmods = get_course_mods($courseid); - if (empty($rawmods)) { - return []; - } - - $mods = []; - if ($usecache) { - // Get existing cache. - $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); - $coursemodinfo = $cachecoursemodinfo->get($courseid); - if ($coursemodinfo !== false) { - $mods = $coursemodinfo->modinfo; - } - } - - $courseformat = course_get_format($course); - - if ($sections = $DB->get_records('course_sections', array('course' => $courseid), - 'section ASC', 'id,section,sequence,visible')) { - // First check and correct obvious mismatches between course_sections.sequence and course_modules.section. - if ($errormessages = course_integrity_check($courseid, $rawmods, $sections)) { - debugging(join('
', $errormessages)); - $rawmods = get_course_mods($courseid); - $sections = $DB->get_records('course_sections', array('course' => $courseid), - 'section ASC', 'id,section,sequence,visible'); - } - // Build array of activities. - foreach ($sections as $section) { - if (!empty($section->sequence)) { - $cmids = explode(",", $section->sequence); - $numberofmods = count($cmids); - for ($order = 0; $order < $numberofmods; $order++) { - $cmid = $cmids[$order]; - // Activity does not exist in the database. - $notexistindb = empty($rawmods[$cmid]); - $activitycached = isset($mods[$cmid]); - if ($activitycached || $notexistindb) { - continue; - } - $modposition = ($order === 0) ? 0 : array_search($cmids[$order - 1], array_keys($mods)) + 1; - $mods = array_slice($mods, 0, $modposition, true) - + [$cmid => new stdClass()] - + array_slice($mods, $modposition, null, true); - - // Adjust visibleoncoursepage, value in DB may not respect format availability. - $rawmods[$cmid]->visibleoncoursepage = (!$rawmods[$cmid]->visible - || $rawmods[$cmid]->visibleoncoursepage - || empty($CFG->allowstealth) - || !$courseformat->allow_stealth_module_visibility($rawmods[$cmid], $section)) ? 1 : 0; - - $mods[$cmid]->id = $rawmods[$cmid]->instance; - $mods[$cmid]->cm = $rawmods[$cmid]->id; - $mods[$cmid]->mod = $rawmods[$cmid]->modname; - - // Oh dear. Inconsistent names left here for backward compatibility. - $mods[$cmid]->section = $section->section; - $mods[$cmid]->sectionid = $rawmods[$cmid]->section; - - $mods[$cmid]->module = $rawmods[$cmid]->module; - $mods[$cmid]->added = $rawmods[$cmid]->added; - $mods[$cmid]->score = $rawmods[$cmid]->score; - $mods[$cmid]->idnumber = $rawmods[$cmid]->idnumber; - $mods[$cmid]->visible = $rawmods[$cmid]->visible; - $mods[$cmid]->visibleoncoursepage = $rawmods[$cmid]->visibleoncoursepage; - $mods[$cmid]->visibleold = $rawmods[$cmid]->visibleold; - $mods[$cmid]->groupmode = $rawmods[$cmid]->groupmode; - $mods[$cmid]->groupingid = $rawmods[$cmid]->groupingid; - $mods[$cmid]->indent = $rawmods[$cmid]->indent; - $mods[$cmid]->completion = $rawmods[$cmid]->completion; - $mods[$cmid]->extra = ""; - $mods[$cmid]->completiongradeitemnumber = - $rawmods[$cmid]->completiongradeitemnumber; - $mods[$cmid]->completionpassgrade = $rawmods[$cmid]->completionpassgrade; - $mods[$cmid]->completionview = $rawmods[$cmid]->completionview; - $mods[$cmid]->completionexpected = $rawmods[$cmid]->completionexpected; - $mods[$cmid]->showdescription = $rawmods[$cmid]->showdescription; - $mods[$cmid]->availability = $rawmods[$cmid]->availability; - $mods[$cmid]->deletioninprogress = $rawmods[$cmid]->deletioninprogress; - $mods[$cmid]->downloadcontent = $rawmods[$cmid]->downloadcontent; - - $modname = $mods[$cmid]->mod; - $functionname = $modname . "_get_coursemodule_info"; - - if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) { - continue; - } - - include_once("$CFG->dirroot/mod/$modname/lib.php"); - - if ($hasfunction = function_exists($functionname)) { - if ($info = $functionname($rawmods[$cmid])) { - if (!empty($info->icon)) { - $mods[$cmid]->icon = $info->icon; - } - if (!empty($info->iconcomponent)) { - $mods[$cmid]->iconcomponent = $info->iconcomponent; - } - if (!empty($info->name)) { - $mods[$cmid]->name = $info->name; - } - if ($info instanceof cached_cm_info) { - // When using cached_cm_info you can include three new fields. - // That aren't available for legacy code. - if (!empty($info->content)) { - $mods[$cmid]->content = $info->content; - } - if (!empty($info->extraclasses)) { - $mods[$cmid]->extraclasses = $info->extraclasses; - } - if (!empty($info->iconurl)) { - // Convert URL to string as it's easier to store. - // Also serialized object contains \0 byte, - // ... and can not be written to Postgres DB. - $url = new moodle_url($info->iconurl); - $mods[$cmid]->iconurl = $url->out(false); - } - if (!empty($info->onclick)) { - $mods[$cmid]->onclick = $info->onclick; - } - if (!empty($info->customdata)) { - $mods[$cmid]->customdata = $info->customdata; - } - } else { - // When using a stdclass, the (horrible) deprecated ->extra field, - // ... that is available for BC. - if (!empty($info->extra)) { - $mods[$cmid]->extra = $info->extra; - } - } - } - } - // When there is no modname_get_coursemodule_info function, - // ... but showdescriptions is enabled, then we use the 'intro', - // ... and 'introformat' fields in the module table. - if (!$hasfunction && $rawmods[$cmid]->showdescription) { - if ($modvalues = $DB->get_record($rawmods[$cmid]->modname, - ['id' => $rawmods[$cmid]->instance], 'name, intro, introformat')) { - // Set content from intro and introformat. Filters are disabled. - // Because we filter it with format_text at display time. - $mods[$cmid]->content = format_module_intro($rawmods[$cmid]->modname, - $modvalues, $rawmods[$cmid]->id, false); - - // To save making another query just below, put name in here. - $mods[$cmid]->name = $modvalues->name; - } - } - if (!isset($mods[$cmid]->name)) { - $mods[$cmid]->name = $DB->get_field($rawmods[$cmid]->modname, "name", - ["id" => $rawmods[$cmid]->instance]); - } - - // Minimise the database size by unsetting default options when they are 'empty'. - // This list corresponds to code in the cm_info constructor. - foreach (['idnumber', 'groupmode', 'groupingid', - 'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content', - 'icon', 'iconcomponent', 'customdata', 'availability', 'completionview', - 'completionexpected', 'score', 'showdescription', 'deletioninprogress'] as $property) { - if (property_exists($mods[$cmid], $property) && - empty($mods[$cmid]->{$property})) { - unset($mods[$cmid]->{$property}); - } - } - // Special case: this value is usually set to null, but may be 0. - if (property_exists($mods[$cmid], 'completiongradeitemnumber') && - is_null($mods[$cmid]->completiongradeitemnumber)) { - unset($mods[$cmid]->completiongradeitemnumber); - } - } - } - } - } - return $mods; -} - /** * Returns an array where the key is the module name (component name without 'mod_') * and the value is a lang_string object with a human-readable string. diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index c75d8551880..f9c63c5aa02 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -1063,7 +1063,7 @@ class courselib_test extends advanced_testcase { // Build course cache. get_fast_modinfo($course->id); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; @@ -1077,7 +1077,7 @@ class courselib_test extends advanced_testcase { // Move section. move_section_to($course, 2, 3); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; diff --git a/course/upgrade.txt b/course/upgrade.txt index e9267c4ded2..11dbd088635 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -92,6 +92,8 @@ course formats don't have their own renderer. * The following functions have been deprecated in favour partial course cache rebuild: - course_purge_section_cache (replaced by course_modinfo::purge_course_section_cache_by_id() and course_modinfo::purge_course_section_cache_by_number()) - course_purge_module_cache (replaced by course_modinfo::purge_course_module_cache()) + - get_array_of_activities (replaced by course_modinfo::get_array_of_activities() for better performance) +* New course_modinfo::purge_course_cache function was created to purge the cache of a given course. === 3.11 === * A new callback xxx_coursemodule_definition_after_data that allows plugins to extend activity forms after the data is set. diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index 5d95ab6a96f..9d9208c882c 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -3729,3 +3729,18 @@ function print_grade_plugin_selector($plugin_info, $active_type, $active_plugin, course_modinfo::purge_course_module_cache($courseid, $cmid); } } + +/** + * For a given course, returns an array of course activity objects + * Each item in the array contains he following properties: + * + * @param int $courseid course id + * @param bool $usecache get activities from cache if modinfo exists when $usecache is true + * @return array list of activities + * @deprecated since Moodle 4.0. Please use {@link course_modinfo::get_array_of_activities()} instead. + */ +function get_array_of_activities(int $courseid, bool $usecache = false): array { + debugging(__FUNCTION__ . '() is deprecated. ' . 'Please use course_modinfo::get_array_of_activities() instead.', + DEBUG_DEVELOPER); + return course_modinfo::get_array_of_activities(get_course($courseid), $usecache); +} diff --git a/lib/modinfolib.php b/lib/modinfolib.php index 4a69dc8ef8f..bf20367db5d 100644 --- a/lib/modinfolib.php +++ b/lib/modinfolib.php @@ -476,13 +476,13 @@ class course_modinfo { // Retrieve modinfo from cache. If not present or cacherev mismatches, call rebuild and retrieve again. $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev); - if (!$coursemodinfo) { + if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) { $lock = self::get_course_cache_lock($course->id); try { // Only actually do the build if it's still needed after getting the lock (not if // somebody else, who might have been holding the lock, built it already). $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev); - if (!$coursemodinfo) { + if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) { $coursemodinfo = self::inner_build_course_cache($course, $lock); } } finally { @@ -553,7 +553,6 @@ class course_modinfo { $this->instances[$cm->modname] = array(); } $this->instances[$cm->modname][$cm->instance] = $cm; - ksort($this->instances[$cm->modname]); $this->cms[$cm->id] = $cm; // Reconstruct sections. This works because modules are stored in order @@ -563,8 +562,6 @@ class course_modinfo { $this->sections[$cm->sectionnum][] = $cm->id; } - ksort($this->cms); - ksort($this->instances); // Expand section objects $this->sectioninfo = array(); foreach ($coursemodinfo->sectioncache as $number => $data) { @@ -605,7 +602,7 @@ class course_modinfo { if ($usecache) { $cachecoursemodinfo = \cache::make('core', 'coursemodinfo'); - $coursemodinfo = $cachecoursemodinfo->get($course->id); + $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev); if ($coursemodinfo !== false) { $compressedsections = $coursemodinfo->sectioncache; } @@ -709,7 +706,7 @@ class course_modinfo { // Retrieve all information about activities and sections. $coursemodinfo = new stdClass(); - $coursemodinfo->modinfo = get_array_of_activities($course->id, $partialrebuild); + $coursemodinfo->modinfo = self::get_array_of_activities($course, $partialrebuild); $coursemodinfo->sectioncache = self::build_course_section_cache($course, $partialrebuild); foreach (self::$cachedfields as $key) { $coursemodinfo->$key = $course->$key; @@ -727,20 +724,21 @@ class course_modinfo { * @param int $sectionid The section _id_ to purge */ public static function purge_course_section_cache_by_id(int $courseid, int $sectionid): void { + $course = get_course($courseid); $cache = cache::make('core', 'coursemodinfo'); - $cache->acquire_lock($courseid); - $coursemodinfo = $cache->get($courseid); + $cache->acquire_lock($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); if ($coursemodinfo !== false) { foreach ($coursemodinfo->sectioncache as $sectionno => $sectioncache) { if ($sectioncache->id == $sectionid) { $coursemodinfo->cacherev = -1; unset($coursemodinfo->sectioncache[$sectionno]); - $cache->set($courseid, $coursemodinfo); + $cache->set_versioned($course->id, $course->cacherev, $coursemodinfo); break; } } } - $cache->release_lock($courseid); + $cache->release_lock($course->id); } /** @@ -750,15 +748,16 @@ class course_modinfo { * @param int $sectionno The section number to purge */ public static function purge_course_section_cache_by_number(int $courseid, int $sectionno): void { + $course = get_course($courseid); $cache = cache::make('core', 'coursemodinfo'); - $cache->acquire_lock($courseid); - $coursemodinfo = $cache->get($courseid); + $cache->acquire_lock($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); if ($coursemodinfo !== false && array_key_exists($sectionno, $coursemodinfo->sectioncache)) { $coursemodinfo->cacherev = -1; unset($coursemodinfo->sectioncache[$sectionno]); - $cache->set($courseid, $coursemodinfo); + $cache->set_versioned($course->id, $course->cacherev, $coursemodinfo); } - $cache->release_lock($courseid); + $cache->release_lock($course->id); } /** @@ -768,16 +767,212 @@ class course_modinfo { * @param int $cmid Course module id */ public static function purge_course_module_cache(int $courseid, int $cmid): void { + $course = get_course($courseid); $cache = cache::make('core', 'coursemodinfo'); - $cache->acquire_lock($courseid); - $coursemodinfo = $cache->get($courseid); + $cache->acquire_lock($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); $hascache = ($coursemodinfo !== false) && array_key_exists($cmid, $coursemodinfo->modinfo); if ($hascache) { $coursemodinfo->cacherev = -1; unset($coursemodinfo->modinfo[$cmid]); - $cache->set($courseid, $coursemodinfo); + $cache->set_versioned($course->id, $course->cacherev, $coursemodinfo); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); } - $cache->release_lock($courseid); + $cache->release_lock($course->id); + } + + /** + * For a given course, returns an array of course activity objects + * + * @param stdClass $course Course object + * @param bool $usecache get activities from cache if modinfo exists when $usecache is true + * @return array list of activities + */ + public static function get_array_of_activities(stdClass $course, bool $usecache = false): array { + global $CFG, $DB; + + if (empty($course)) { + throw new moodle_exception('courseidnotfound'); + } + + $rawmods = get_course_mods($course->id); + if (empty($rawmods)) { + return []; + } + + $mods = []; + if ($usecache) { + // Get existing cache. + $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); + $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev); + if ($coursemodinfo !== false) { + $mods = $coursemodinfo->modinfo; + } + } + + $courseformat = course_get_format($course); + + if ($sections = $DB->get_records('course_sections', ['course' => $course->id], + 'section ASC', 'id,section,sequence,visible')) { + // First check and correct obvious mismatches between course_sections.sequence and course_modules.section. + if ($errormessages = course_integrity_check($course->id, $rawmods, $sections)) { + debugging(join('
', $errormessages)); + $rawmods = get_course_mods($course->id); + $sections = $DB->get_records('course_sections', ['course' => $course->id], + 'section ASC', 'id,section,sequence,visible'); + } + // Build array of activities. + foreach ($sections as $section) { + if (!empty($section->sequence)) { + $cmids = explode(",", $section->sequence); + $numberofmods = count($cmids); + foreach ($cmids as $cmid) { + // Activity does not exist in the database. + $notexistindb = empty($rawmods[$cmid]); + $activitycached = isset($mods[$cmid]); + if ($activitycached || $notexistindb) { + continue; + } + + // Adjust visibleoncoursepage, value in DB may not respect format availability. + $rawmods[$cmid]->visibleoncoursepage = (!$rawmods[$cmid]->visible + || $rawmods[$cmid]->visibleoncoursepage + || empty($CFG->allowstealth) + || !$courseformat->allow_stealth_module_visibility($rawmods[$cmid], $section)) ? 1 : 0; + + $mods[$cmid] = new stdClass(); + $mods[$cmid]->id = $rawmods[$cmid]->instance; + $mods[$cmid]->cm = $rawmods[$cmid]->id; + $mods[$cmid]->mod = $rawmods[$cmid]->modname; + + // Oh dear. Inconsistent names left here for backward compatibility. + $mods[$cmid]->section = $section->section; + $mods[$cmid]->sectionid = $rawmods[$cmid]->section; + + $mods[$cmid]->module = $rawmods[$cmid]->module; + $mods[$cmid]->added = $rawmods[$cmid]->added; + $mods[$cmid]->score = $rawmods[$cmid]->score; + $mods[$cmid]->idnumber = $rawmods[$cmid]->idnumber; + $mods[$cmid]->visible = $rawmods[$cmid]->visible; + $mods[$cmid]->visibleoncoursepage = $rawmods[$cmid]->visibleoncoursepage; + $mods[$cmid]->visibleold = $rawmods[$cmid]->visibleold; + $mods[$cmid]->groupmode = $rawmods[$cmid]->groupmode; + $mods[$cmid]->groupingid = $rawmods[$cmid]->groupingid; + $mods[$cmid]->indent = $rawmods[$cmid]->indent; + $mods[$cmid]->completion = $rawmods[$cmid]->completion; + $mods[$cmid]->extra = ""; + $mods[$cmid]->completiongradeitemnumber = + $rawmods[$cmid]->completiongradeitemnumber; + $mods[$cmid]->completionpassgrade = $rawmods[$cmid]->completionpassgrade; + $mods[$cmid]->completionview = $rawmods[$cmid]->completionview; + $mods[$cmid]->completionexpected = $rawmods[$cmid]->completionexpected; + $mods[$cmid]->showdescription = $rawmods[$cmid]->showdescription; + $mods[$cmid]->availability = $rawmods[$cmid]->availability; + $mods[$cmid]->deletioninprogress = $rawmods[$cmid]->deletioninprogress; + $mods[$cmid]->downloadcontent = $rawmods[$cmid]->downloadcontent; + + $modname = $mods[$cmid]->mod; + $functionname = $modname . "_get_coursemodule_info"; + + if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) { + continue; + } + + include_once("$CFG->dirroot/mod/$modname/lib.php"); + + if ($hasfunction = function_exists($functionname)) { + if ($info = $functionname($rawmods[$cmid])) { + if (!empty($info->icon)) { + $mods[$cmid]->icon = $info->icon; + } + if (!empty($info->iconcomponent)) { + $mods[$cmid]->iconcomponent = $info->iconcomponent; + } + if (!empty($info->name)) { + $mods[$cmid]->name = $info->name; + } + if ($info instanceof cached_cm_info) { + // When using cached_cm_info you can include three new fields. + // That aren't available for legacy code. + if (!empty($info->content)) { + $mods[$cmid]->content = $info->content; + } + if (!empty($info->extraclasses)) { + $mods[$cmid]->extraclasses = $info->extraclasses; + } + if (!empty($info->iconurl)) { + // Convert URL to string as it's easier to store. + // Also serialized object contains \0 byte, + // ... and can not be written to Postgres DB. + $url = new moodle_url($info->iconurl); + $mods[$cmid]->iconurl = $url->out(false); + } + if (!empty($info->onclick)) { + $mods[$cmid]->onclick = $info->onclick; + } + if (!empty($info->customdata)) { + $mods[$cmid]->customdata = $info->customdata; + } + } else { + // When using a stdclass, the (horrible) deprecated ->extra field, + // ... that is available for BC. + if (!empty($info->extra)) { + $mods[$cmid]->extra = $info->extra; + } + } + } + } + // When there is no modname_get_coursemodule_info function, + // ... but showdescriptions is enabled, then we use the 'intro', + // ... and 'introformat' fields in the module table. + if (!$hasfunction && $rawmods[$cmid]->showdescription) { + if ($modvalues = $DB->get_record($rawmods[$cmid]->modname, + ['id' => $rawmods[$cmid]->instance], 'name, intro, introformat')) { + // Set content from intro and introformat. Filters are disabled. + // Because we filter it with format_text at display time. + $mods[$cmid]->content = format_module_intro($rawmods[$cmid]->modname, + $modvalues, $rawmods[$cmid]->id, false); + + // To save making another query just below, put name in here. + $mods[$cmid]->name = $modvalues->name; + } + } + if (!isset($mods[$cmid]->name)) { + $mods[$cmid]->name = $DB->get_field($rawmods[$cmid]->modname, "name", + ["id" => $rawmods[$cmid]->instance]); + } + + // Minimise the database size by unsetting default options when they are 'empty'. + // This list corresponds to code in the cm_info constructor. + foreach (['idnumber', 'groupmode', 'groupingid', + 'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content', + 'icon', 'iconcomponent', 'customdata', 'availability', 'completionview', + 'completionexpected', 'score', 'showdescription', 'deletioninprogress'] as $property) { + if (property_exists($mods[$cmid], $property) && + empty($mods[$cmid]->{$property})) { + unset($mods[$cmid]->{$property}); + } + } + // Special case: this value is usually set to null, but may be 0. + if (property_exists($mods[$cmid], 'completiongradeitemnumber') && + is_null($mods[$cmid]->completiongradeitemnumber)) { + unset($mods[$cmid]->completiongradeitemnumber); + } + } + } + } + } + return $mods; + } + + /** + * Purge the cache of a given course + * + * @param int $courseid Course id + */ + public static function purge_course_cache(int $courseid): void { + $cachemodinfo = cache::make('core', 'coursemodinfo'); + $cachemodinfo->delete($courseid); } } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index ee60a46c6e3..eb4c126f75a 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -5347,8 +5347,7 @@ function remove_course_contents($courseid, $showfeedback = true, array $options fulldelete($CFG->dataroot.'/'.$course->id); // Delete from cache to reduce the cache size especially makes sense in case of bulk course deletion. - $cachemodinfo = cache::make('core', 'coursemodinfo'); - $cachemodinfo->delete($courseid); + course_modinfo::purge_course_cache($courseid); // Trigger a course content deleted event. $event = \core\event\course_content_deleted::create(array( diff --git a/lib/tests/modinfolib_test.php b/lib/tests/modinfolib_test.php index 633676201b6..da19910af82 100644 --- a/lib/tests/modinfolib_test.php +++ b/lib/tests/modinfolib_test.php @@ -367,7 +367,7 @@ class modinfolib_test extends advanced_testcase { $this->assertEquals($USER->id, $modinfo->userid); $this->assertEquals(array(0 => array($forum0->cmid, $assign0->cmid), 1 => array($forum1->cmid, $assign1->cmid, $page1->cmid), 3 => array($page3->cmid)), $modinfo->sections); - $this->assertEquals(array('assign', 'forum', 'page'), array_keys($modinfo->instances)); + $this->assertEquals(array('forum', 'assign', 'page'), array_keys($modinfo->instances)); $this->assertEquals(array($assign0->id, $assign1->id), array_keys($modinfo->instances['assign'])); $this->assertEquals(array($forum0->id, $forum1->id), array_keys($modinfo->instances['forum'])); $this->assertEquals(array($page1->id, $page3->id), array_keys($modinfo->instances['page'])); @@ -1023,7 +1023,7 @@ class modinfolib_test extends advanced_testcase { // Build course cache. get_fast_modinfo($course->id); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; @@ -1037,7 +1037,7 @@ class modinfolib_test extends advanced_testcase { // Purge cache for the section by id. course_modinfo::purge_course_section_cache_by_id($course->id, $sectioncaches[1]->id); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; @@ -1069,7 +1069,7 @@ class modinfolib_test extends advanced_testcase { // Build course cache. get_fast_modinfo($course->id); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; @@ -1083,7 +1083,7 @@ class modinfolib_test extends advanced_testcase { // Purge cache for the section with section number is 1. course_modinfo::purge_course_section_cache_by_number($course->id, 1); // Get the course modinfo cache. - $coursemodinfo = $cache->get($course->id); + $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); // Get the section cache. $sectioncaches = $coursemodinfo->sectioncache; diff --git a/mod/page/lib.php b/mod/page/lib.php index dae27693bb7..bda4a4fd616 100644 --- a/mod/page/lib.php +++ b/mod/page/lib.php @@ -201,7 +201,7 @@ function page_delete_instance($id) { * "extra" information that may be needed when printing * this activity in a course listing. * - * See {@link get_array_of_activities()} in course/lib.php + * See {@link course_modinfo::get_array_of_activities()} * * @param stdClass $coursemodule * @return cached_cm_info Info to customise main page display diff --git a/mod/resource/lib.php b/mod/resource/lib.php index 726fd47ca4d..563d2422bc5 100644 --- a/mod/resource/lib.php +++ b/mod/resource/lib.php @@ -192,7 +192,7 @@ function resource_delete_instance($id) { * "extra" information that may be needed when printing * this activity in a course listing. * - * See {@link get_array_of_activities()} in course/lib.php + * See {@link course_modinfo::get_array_of_activities()} * * @param stdClass $coursemodule * @return cached_cm_info info diff --git a/mod/url/lib.php b/mod/url/lib.php index 169c8a6784d..dae393ac800 100644 --- a/mod/url/lib.php +++ b/mod/url/lib.php @@ -203,7 +203,7 @@ function url_delete_instance($id) { * "extra" information that may be needed when printing * this activity in a course listing. * - * See {@link get_array_of_activities()} in course/lib.php + * See {@link course_modinfo::get_array_of_activities()} * * @param object $coursemodule * @return cached_cm_info info