mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 16:32:18 +02:00
MDL-55231 core_course: Partial course cache rebuild
This commit is contained in:
parent
e5894c0455
commit
9a900492bc
@ -54,3 +54,64 @@ Feature: availability_completion
|
||||
# Mark page 1 complete
|
||||
When I toggle the manual completion state of "Page 1"
|
||||
Then I should see "Page 2" in the "region-main" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: test completion and course cache rebuild
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| forum | forum 1 | forum 1 | C1 | forum1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I open "forum 1" actions menu
|
||||
And I click on "Edit settings" "link" in the "forum 1" activity
|
||||
And I set the following fields to these values:
|
||||
| Completion tracking | Show activity as complete when conditions are met |
|
||||
| completionview | 1 |
|
||||
| completionpostsenabled | 1 |
|
||||
| completionposts | 2 |
|
||||
And I press "Save and return to course"
|
||||
|
||||
And I add a new discussion to "forum 1" forum with:
|
||||
| Subject | Forum post 1 |
|
||||
| Message | This is the body |
|
||||
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Page" to section "2"
|
||||
And I set the following fields to these values:
|
||||
| Name | Page 2 |
|
||||
| Description | Test |
|
||||
| Page content | Test |
|
||||
And I expand all fieldsets
|
||||
And I press "Add restriction..."
|
||||
And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
|
||||
And I click on ".availability-item .availability-eye img" "css_element"
|
||||
And I set the following fields to these values:
|
||||
| Required completion status | must be marked complete |
|
||||
| cm | forum 1 |
|
||||
And I press "Save and return to course"
|
||||
|
||||
When I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
# Page 2 should not appear yet.
|
||||
Then I should not see "Page 2" in the "region-main" "region"
|
||||
And I click on "forum 1" "link" in the "region-main" "region"
|
||||
# Page 2 should not appear yet.
|
||||
Then I should not see "Page 2" in the "region-main" "region"
|
||||
|
||||
When I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I am on the "forum 1" "forum activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| completionpostsenabled | 0 |
|
||||
And I press "Save and display"
|
||||
|
||||
When I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I click on "forum 1" "link" in the "region-main" "region"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Page 2" in the "region-main" "region"
|
||||
|
@ -292,14 +292,15 @@ class condition extends \core_availability\condition {
|
||||
$updatesection->availability = json_encode($tree->save());
|
||||
$updatesection->timemodified = time();
|
||||
$DB->update_record('course_sections', $updatesection);
|
||||
course_purge_section_cache($section);
|
||||
|
||||
$anychanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure course cache is cleared if required.
|
||||
if ($anychanged) {
|
||||
rebuild_course_cache($courseid, true);
|
||||
// Partial rebuild the sections which have been invalidated.
|
||||
rebuild_course_cache($courseid, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -614,8 +614,10 @@ class dndupload_ajax_processor {
|
||||
$visible = get_fast_modinfo($this->course)->get_section_info($this->section)->visible;
|
||||
|
||||
$DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
|
||||
|
||||
course_purge_module_cache($this->cm);
|
||||
// Rebuild the course cache after update action
|
||||
rebuild_course_cache($this->course->id, true);
|
||||
rebuild_course_cache($this->course->id, true, true);
|
||||
|
||||
$sectionid = course_add_cm_to_section($this->course, $this->cm->id, $this->section);
|
||||
|
||||
|
@ -1009,7 +1009,15 @@ abstract class base {
|
||||
}
|
||||
}
|
||||
if ($needrebuild) {
|
||||
rebuild_course_cache($this->courseid, true);
|
||||
if ($sectionid) {
|
||||
$sectionrecord = $DB->get_record('course_sections', ['id' => $sectionid], 'course, section', MUST_EXIST);
|
||||
course_purge_section_cache($sectionrecord);
|
||||
// Partial rebuild sections that have been invalidated.
|
||||
rebuild_course_cache($this->courseid, true, true);
|
||||
} else {
|
||||
// Full rebuild if sectionid is null.
|
||||
rebuild_course_cache($this->courseid);
|
||||
}
|
||||
}
|
||||
if ($changed) {
|
||||
// Reset internal caches.
|
||||
@ -1277,7 +1285,7 @@ abstract class base {
|
||||
}
|
||||
if (!is_object($section)) {
|
||||
$section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section),
|
||||
'id,section,sequence,summary');
|
||||
'id,course,section,sequence,summary');
|
||||
}
|
||||
if (!$section || !$section->section) {
|
||||
// Not possible to delete 0-section.
|
||||
@ -1314,7 +1322,9 @@ abstract class base {
|
||||
// Delete section and it's format options.
|
||||
$DB->delete_records('course_format_options', array('sectionid' => $section->id));
|
||||
$DB->delete_records('course_sections', array('id' => $section->id));
|
||||
rebuild_course_cache($course->id, true);
|
||||
course_purge_section_cache($section);
|
||||
// Partial rebuild section cache that has been purged.
|
||||
rebuild_course_cache($course->id, true, true);
|
||||
|
||||
// Delete section summary files.
|
||||
$context = \context_course::instance($course->id);
|
||||
|
364
course/lib.php
364
course/lib.php
@ -389,8 +389,12 @@ function course_integrity_check($courseid, $rawmods = null, $sections = null, $f
|
||||
/**
|
||||
* 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($courseid) {
|
||||
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)
|
||||
@ -406,12 +410,21 @@ function get_array_of_activities($courseid) {
|
||||
throw new moodle_exception('courseidnotfound');
|
||||
}
|
||||
|
||||
$mod = array();
|
||||
|
||||
$rawmods = get_course_mods($courseid);
|
||||
if (empty($rawmods)) {
|
||||
return $mod; // always return array
|
||||
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),
|
||||
@ -423,140 +436,152 @@ function get_array_of_activities($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)) {
|
||||
$sequence = explode(",", $section->sequence);
|
||||
foreach ($sequence as $seq) {
|
||||
if (empty($rawmods[$seq])) {
|
||||
continue;
|
||||
}
|
||||
// Adjust visibleoncoursepage, value in DB may not respect format availability.
|
||||
$rawmods[$seq]->visibleoncoursepage = (!$rawmods[$seq]->visible
|
||||
|| $rawmods[$seq]->visibleoncoursepage
|
||||
|| empty($CFG->allowstealth)
|
||||
|| !$courseformat->allow_stealth_module_visibility($rawmods[$seq], $section)) ? 1 : 0;
|
||||
|
||||
// Create an object that will be cached.
|
||||
$mod[$seq] = new stdClass();
|
||||
$mod[$seq]->id = $rawmods[$seq]->instance;
|
||||
$mod[$seq]->cm = $rawmods[$seq]->id;
|
||||
$mod[$seq]->mod = $rawmods[$seq]->modname;
|
||||
// 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 not exist in 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.
|
||||
$mod[$seq]->section = $section->section;
|
||||
$mod[$seq]->sectionid = $rawmods[$seq]->section;
|
||||
$mods[$cmid]->section = $section->section;
|
||||
$mods[$cmid]->sectionid = $rawmods[$cmid]->section;
|
||||
|
||||
$mod[$seq]->module = $rawmods[$seq]->module;
|
||||
$mod[$seq]->added = $rawmods[$seq]->added;
|
||||
$mod[$seq]->score = $rawmods[$seq]->score;
|
||||
$mod[$seq]->idnumber = $rawmods[$seq]->idnumber;
|
||||
$mod[$seq]->visible = $rawmods[$seq]->visible;
|
||||
$mod[$seq]->visibleoncoursepage = $rawmods[$seq]->visibleoncoursepage;
|
||||
$mod[$seq]->visibleold = $rawmods[$seq]->visibleold;
|
||||
$mod[$seq]->groupmode = $rawmods[$seq]->groupmode;
|
||||
$mod[$seq]->groupingid = $rawmods[$seq]->groupingid;
|
||||
$mod[$seq]->indent = $rawmods[$seq]->indent;
|
||||
$mod[$seq]->completion = $rawmods[$seq]->completion;
|
||||
$mod[$seq]->extra = "";
|
||||
$mod[$seq]->completiongradeitemnumber =
|
||||
$rawmods[$seq]->completiongradeitemnumber;
|
||||
$mod[$seq]->completionpassgrade = $rawmods[$seq]->completionpassgrade;
|
||||
$mod[$seq]->completionview = $rawmods[$seq]->completionview;
|
||||
$mod[$seq]->completionexpected = $rawmods[$seq]->completionexpected;
|
||||
$mod[$seq]->showdescription = $rawmods[$seq]->showdescription;
|
||||
$mod[$seq]->availability = $rawmods[$seq]->availability;
|
||||
$mod[$seq]->deletioninprogress = $rawmods[$seq]->deletioninprogress;
|
||||
$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;
|
||||
|
||||
$modname = $mod[$seq]->mod;
|
||||
$functionname = $modname."_get_coursemodule_info";
|
||||
$modname = $mods[$cmid]->mod;
|
||||
$functionname = $modname . "_get_coursemodule_info";
|
||||
|
||||
if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
|
||||
continue;
|
||||
}
|
||||
if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
include_once("$CFG->dirroot/mod/$modname/lib.php");
|
||||
include_once("$CFG->dirroot/mod/$modname/lib.php");
|
||||
|
||||
if ($hasfunction = function_exists($functionname)) {
|
||||
if ($info = $functionname($rawmods[$seq])) {
|
||||
if (!empty($info->icon)) {
|
||||
$mod[$seq]->icon = $info->icon;
|
||||
}
|
||||
if (!empty($info->iconcomponent)) {
|
||||
$mod[$seq]->iconcomponent = $info->iconcomponent;
|
||||
}
|
||||
if (!empty($info->name)) {
|
||||
$mod[$seq]->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)) {
|
||||
$mod[$seq]->content = $info->content;
|
||||
}
|
||||
if (!empty($info->extraclasses)) {
|
||||
$mod[$seq]->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);
|
||||
$mod[$seq]->iconurl = $url->out(false);
|
||||
}
|
||||
if (!empty($info->onclick)) {
|
||||
$mod[$seq]->onclick = $info->onclick;
|
||||
}
|
||||
if (!empty($info->customdata)) {
|
||||
$mod[$seq]->customdata = $info->customdata;
|
||||
}
|
||||
} else {
|
||||
// When using a stdclass, the (horrible) deprecated ->extra field
|
||||
// is available for BC
|
||||
if (!empty($info->extra)) {
|
||||
$mod[$seq]->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[$seq]->showdescription) {
|
||||
if ($modvalues = $DB->get_record($rawmods[$seq]->modname,
|
||||
array('id' => $rawmods[$seq]->instance), 'name, intro, introformat')) {
|
||||
// Set content from intro and introformat. Filters are disabled
|
||||
// because we filter it with format_text at display time
|
||||
$mod[$seq]->content = format_module_intro($rawmods[$seq]->modname,
|
||||
$modvalues, $rawmods[$seq]->id, false);
|
||||
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
|
||||
$mod[$seq]->name = $modvalues->name;
|
||||
}
|
||||
}
|
||||
if (!isset($mod[$seq]->name)) {
|
||||
$mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
|
||||
}
|
||||
// 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 (array('idnumber', 'groupmode', 'groupingid',
|
||||
// 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($mod[$seq], $property) &&
|
||||
empty($mod[$seq]->{$property})) {
|
||||
unset($mod[$seq]->{$property});
|
||||
}
|
||||
}
|
||||
// Special case: this value is usually set to null, but may be 0
|
||||
if (property_exists($mod[$seq], 'completiongradeitemnumber') &&
|
||||
is_null($mod[$seq]->completiongradeitemnumber)) {
|
||||
unset($mod[$seq]->completiongradeitemnumber);
|
||||
}
|
||||
}
|
||||
'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 $mod;
|
||||
return $mods;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -814,11 +839,7 @@ function course_add_cm_to_section($courseorid, $cmid, $sectionnum, $beforemod =
|
||||
}
|
||||
$DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
|
||||
$DB->set_field('course_modules', 'section', $section->id, array('id' => $cmid));
|
||||
if (is_object($courseorid)) {
|
||||
rebuild_course_cache($courseorid->id, true);
|
||||
} else {
|
||||
rebuild_course_cache($courseorid, true);
|
||||
}
|
||||
rebuild_course_cache($courseid, true);
|
||||
return $section->id; // Return course_sections ID that was used.
|
||||
}
|
||||
|
||||
@ -837,7 +858,8 @@ function set_coursemodule_groupmode($id, $groupmode) {
|
||||
$cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,groupmode', MUST_EXIST);
|
||||
if ($cm->groupmode != $groupmode) {
|
||||
$DB->set_field('course_modules', 'groupmode', $groupmode, array('id' => $cm->id));
|
||||
rebuild_course_cache($cm->course, true);
|
||||
course_purge_module_cache($cm);
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
}
|
||||
return ($cm->groupmode != $groupmode);
|
||||
}
|
||||
@ -847,7 +869,8 @@ function set_coursemodule_idnumber($id, $idnumber) {
|
||||
$cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,idnumber', MUST_EXIST);
|
||||
if ($cm->idnumber != $idnumber) {
|
||||
$DB->set_field('course_modules', 'idnumber', $idnumber, array('id' => $cm->id));
|
||||
rebuild_course_cache($cm->course, true);
|
||||
course_purge_module_cache($cm);
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
}
|
||||
return ($cm->idnumber != $idnumber);
|
||||
}
|
||||
@ -924,7 +947,8 @@ function set_coursemodule_visible($id, $visible, $visibleoncoursepage = 1) {
|
||||
}
|
||||
}
|
||||
|
||||
rebuild_course_cache($cm->course, true);
|
||||
course_purge_module_cache($cm);
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -961,7 +985,8 @@ function set_coursemodule_name($id, $name) {
|
||||
$DB->update_record($cm->modname, $module);
|
||||
$cm->name = $module->name;
|
||||
\core\event\course_module_updated::create_from_cm($cm)->trigger();
|
||||
rebuild_course_cache($cm->course, true);
|
||||
course_purge_module_cache($cm);
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
|
||||
// Attempt to update the grade item if relevant.
|
||||
$grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance));
|
||||
@ -1113,13 +1138,14 @@ function course_delete_module($cmid, $async = false) {
|
||||
'context' => $modcontext,
|
||||
'objectid' => $cm->id,
|
||||
'other' => array(
|
||||
'modulename' => $modulename,
|
||||
'modulename' => $modulename,
|
||||
'instanceid' => $cm->instance,
|
||||
)
|
||||
));
|
||||
$event->add_record_snapshot('course_modules', $cm);
|
||||
$event->trigger();
|
||||
rebuild_course_cache($cm->course, true);
|
||||
course_purge_module_cache($cm);
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1376,17 +1402,23 @@ function move_section_to($course, $section, $destination, $ignorenumsections = f
|
||||
|
||||
$movedsections = reorder_sections($sections, $section, $destination);
|
||||
|
||||
$sectioninfo = new stdClass;
|
||||
$sectioninfo->course = $course->id;
|
||||
// Update all sections. Do this in 2 steps to avoid breaking database
|
||||
// uniqueness constraint
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
foreach ($movedsections as $id => $position) {
|
||||
if ($sections[$id] !== $position) {
|
||||
$DB->set_field('course_sections', 'section', -$position, array('id' => $id));
|
||||
$DB->set_field('course_sections', 'section', -$position, ['id' => $id]);
|
||||
$sectioninfo->section = $id;
|
||||
course_purge_section_cache($sectioninfo);
|
||||
}
|
||||
}
|
||||
foreach ($movedsections as $id => $position) {
|
||||
if ($sections[$id] !== $position) {
|
||||
$DB->set_field('course_sections', 'section', $position, array('id' => $id));
|
||||
$DB->set_field('course_sections', 'section', $position, ['id' => $id]);
|
||||
$sectioninfo->section = $id;
|
||||
course_purge_section_cache($sectioninfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1394,14 +1426,14 @@ function move_section_to($course, $section, $destination, $ignorenumsections = f
|
||||
// Adjust the higlighted section location if we move something over it either direction.
|
||||
if ($section == $course->marker) {
|
||||
course_set_marker($course->id, $destination);
|
||||
} elseif ($section > $course->marker && $course->marker >= $destination) {
|
||||
} else if ($section > $course->marker && $course->marker >= $destination) {
|
||||
course_set_marker($course->id, $course->marker+1);
|
||||
} elseif ($section < $course->marker && $course->marker <= $destination) {
|
||||
} else if ($section < $course->marker && $course->marker <= $destination) {
|
||||
course_set_marker($course->id, $course->marker-1);
|
||||
}
|
||||
|
||||
$transaction->allow_commit();
|
||||
rebuild_course_cache($course->id, true);
|
||||
rebuild_course_cache($course->id, true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1576,7 +1608,8 @@ function course_update_section($course, $section, $data) {
|
||||
$data['id'] = $section->id;
|
||||
$data['timemodified'] = time();
|
||||
$DB->update_record('course_sections', $data);
|
||||
rebuild_course_cache($courseid, true);
|
||||
course_purge_section_cache($section);
|
||||
rebuild_course_cache($courseid, false, true);
|
||||
course_get_format($courseid)->update_section_format_options($data);
|
||||
|
||||
// Update fields of the $section object.
|
||||
@ -1608,11 +1641,13 @@ function course_update_section($course, $section, $data) {
|
||||
} else {
|
||||
// We hide the section, so we hide the module but we store the original state in visibleold.
|
||||
set_coursemodule_visible($moduleid, 0, $cm->visibleoncoursepage);
|
||||
$DB->set_field('course_modules', 'visibleold', $cm->visible, array('id' => $moduleid));
|
||||
$DB->set_field('course_modules', 'visibleold', $cm->visible, ['id' => $moduleid]);
|
||||
course_purge_module_cache($cm);
|
||||
}
|
||||
\core\event\course_module_updated::create_from_cm($cm)->trigger();
|
||||
}
|
||||
}
|
||||
rebuild_course_cache($courseid, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5130,3 +5165,50 @@ function course_output_fragment_new_base_form($args) {
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge the cache of a course section.
|
||||
*
|
||||
* $sectioninfo must have following attributes:
|
||||
* - course: course id
|
||||
* - section: section number
|
||||
*
|
||||
* @param object $sectioninfo section info
|
||||
* @return void
|
||||
*/
|
||||
function course_purge_section_cache(object $sectioninfo): void {
|
||||
$section = $sectioninfo->section;
|
||||
$courseid = $sectioninfo->course;
|
||||
$cache = cache::make('core', 'coursemodinfo');
|
||||
$cache->acquire_lock($courseid);
|
||||
$coursemodinfo = $cache->get($courseid);
|
||||
if (($coursemodinfo !== false) && array_key_exists($section, $coursemodinfo->sectioncache)) {
|
||||
unset($coursemodinfo->sectioncache[$section]);
|
||||
$cache->set($courseid, $coursemodinfo);
|
||||
}
|
||||
$cache->release_lock($courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge the cache of a course module.
|
||||
*
|
||||
* $cm must have following attributes:
|
||||
* - id: cmid
|
||||
* - course: course id
|
||||
*
|
||||
* @param cm_info|stdClass $cm course module
|
||||
* @return void
|
||||
*/
|
||||
function course_purge_module_cache($cm): void {
|
||||
$cmid = $cm->id;
|
||||
$courseid = $cm->course;
|
||||
$cache = cache::make('core', 'coursemodinfo');
|
||||
$cache->acquire_lock($courseid);
|
||||
$coursemodinfo = $cache->get($courseid);
|
||||
$hascache = ($coursemodinfo !== false) && array_key_exists($cmid, $coursemodinfo->modinfo);
|
||||
if ($hascache) {
|
||||
unset($coursemodinfo->modinfo[$cmid]);
|
||||
$cache->set($courseid, $coursemodinfo);
|
||||
}
|
||||
$cache->release_lock($courseid);
|
||||
}
|
||||
|
@ -190,7 +190,9 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
|
||||
|
||||
$DB->set_field('course_modules', 'indent', $cm->indent, array('id'=>$cm->id));
|
||||
|
||||
rebuild_course_cache($cm->course);
|
||||
course_purge_module_cache($cm);
|
||||
// Rebuild invalidated module cache.
|
||||
rebuild_course_cache($cm->course, false, true);
|
||||
|
||||
redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
|
||||
|
||||
|
@ -374,7 +374,11 @@ function edit_module_post_actions($moduleinfo, $course) {
|
||||
$moduleinfo->showgradingmanagement = $showgradingmanagement;
|
||||
}
|
||||
|
||||
rebuild_course_cache($course->id, true);
|
||||
$cminfo = new stdClass();
|
||||
$cminfo->course = $moduleinfo->course;
|
||||
$cminfo->id = $moduleinfo->coursemodule;
|
||||
course_purge_module_cache($cminfo);
|
||||
rebuild_course_cache($course->id, true, true);
|
||||
if ($hasgrades) {
|
||||
grade_regrade_final_grades($course->id);
|
||||
}
|
||||
|
@ -1099,6 +1099,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
|
||||
// Delete section in the middle (2).
|
||||
$this->assertFalse(course_delete_section($course, 2, false));
|
||||
$this->assertEquals(4, course_get_format($course)->get_last_section_number());
|
||||
$this->assertTrue(course_delete_section($course, 2, true));
|
||||
$this->assertFalse($DB->record_exists('course_modules', array('id' => $assign21->cmid)));
|
||||
$this->assertFalse($DB->record_exists('course_modules', array('id' => $assign22->cmid)));
|
||||
|
@ -1085,8 +1085,9 @@ class externallib_test extends externallib_advanced_testcase {
|
||||
array('course' => $course->id, 'intro' => 'forum completion tracking auto', 'trackingtype' => 2),
|
||||
array('showdescription' => true, 'completionview' => 1, 'completion' => COMPLETION_TRACKING_AUTOMATIC));
|
||||
$forumcompleteautocm = get_coursemodule_from_id('forum', $forumcompleteauto->cmid);
|
||||
|
||||
rebuild_course_cache($course->id, true);
|
||||
$sectionrecord = $DB->get_record('course_sections', $conditions);
|
||||
course_purge_section_cache($sectionrecord);
|
||||
rebuild_course_cache($course->id, true, true);
|
||||
|
||||
return array($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm, $forumcompleteautocm);
|
||||
}
|
||||
|
@ -221,8 +221,16 @@ $definitions = array(
|
||||
'simplekeys' => true,
|
||||
'ttl' => 3600,
|
||||
),
|
||||
// Accumulated information about course modules and sections used to print course view page (user-independed).
|
||||
// Used in function get_fast_modinfo(), reset in function rebuild_course_cache().
|
||||
// Accumulated information about course modules and sections used to print course view page (user-independent).
|
||||
// Used in functions:
|
||||
// - course_modinfo::build_course_section_cache()
|
||||
// - course_modinfo::inner_build_course_cache()
|
||||
// - get_array_of_activities()
|
||||
// Reset/update in functions:
|
||||
// - rebuild_course_cache()
|
||||
// - course_purge_module_cache()
|
||||
// - course_purge_section_cache()
|
||||
// - remove_course_contents().
|
||||
'coursemodinfo' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
|
@ -514,7 +514,7 @@ class course_modinfo {
|
||||
' does not have context. Rebuilding cache for course '. $course->id);
|
||||
// Re-request the course record from DB as well, don't use get_course() here.
|
||||
$course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
|
||||
$coursemodinfo = self::build_course_cache($course);
|
||||
$coursemodinfo = self::build_course_cache($course, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -553,6 +553,7 @@ 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
|
||||
@ -562,6 +563,8 @@ 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) {
|
||||
@ -588,23 +591,37 @@ class course_modinfo {
|
||||
* in some other way.)
|
||||
*
|
||||
* @param stdClass $course Course object (must contain fields
|
||||
* @param boolean $usecache use cached section info if exists, use true for partial course rebuild
|
||||
* @return array Information about sections, indexed by section number (not id)
|
||||
*/
|
||||
protected static function build_course_section_cache($course) {
|
||||
protected static function build_course_section_cache(\stdClass $course, bool $usecache = false): array {
|
||||
global $DB;
|
||||
|
||||
// Get section data
|
||||
$sections = $DB->get_records('course_sections', array('course' => $course->id), 'section',
|
||||
'section, id, course, name, summary, summaryformat, sequence, visible, availability');
|
||||
$compressedsections = array();
|
||||
$compressedsections = [];
|
||||
$courseformat = course_get_format($course);
|
||||
|
||||
if ($usecache) {
|
||||
$cachecoursemodinfo = \cache::make('core', 'coursemodinfo');
|
||||
$coursemodinfo = $cachecoursemodinfo->get($course->id);
|
||||
if ($coursemodinfo !== false) {
|
||||
$compressedsections = $coursemodinfo->sectioncache;
|
||||
}
|
||||
}
|
||||
|
||||
$formatoptionsdef = course_get_format($course)->section_format_options();
|
||||
// Remove unnecessary data and add availability
|
||||
foreach ($sections as $number => $section) {
|
||||
$sectioninfocached = isset($compressedsections[$number]);
|
||||
if ($sectioninfocached) {
|
||||
continue;
|
||||
}
|
||||
// Add cached options from course format to $section object
|
||||
foreach ($formatoptionsdef as $key => $option) {
|
||||
if (!empty($option['cache'])) {
|
||||
$formatoptions = course_get_format($course)->get_format_options($section);
|
||||
$formatoptions = $courseformat->get_format_options($section);
|
||||
if (!array_key_exists('cachedefault', $option) || $option['cachedefault'] !== $formatoptions[$key]) {
|
||||
$section->$key = $formatoptions[$key];
|
||||
}
|
||||
@ -615,6 +632,7 @@ class course_modinfo {
|
||||
section_info::convert_for_section_cache($compressedsections[$number]);
|
||||
}
|
||||
|
||||
ksort($compressedsections);
|
||||
return $compressedsections;
|
||||
}
|
||||
|
||||
@ -652,19 +670,20 @@ class course_modinfo {
|
||||
*
|
||||
* @param stdClass $course object from DB table course. Must have property 'id'
|
||||
* but preferably should have all cached fields.
|
||||
* @param boolean $partialrebuild Indicates it's partial course rebuild or not
|
||||
* @return stdClass object with all cached keys of the course plus fields modinfo and sectioncache.
|
||||
* The same object is stored in MUC
|
||||
* @throws moodle_exception if course is not found (if $course object misses some of the
|
||||
* necessary fields it is re-requested from database)
|
||||
*/
|
||||
public static function build_course_cache($course) {
|
||||
public static function build_course_cache(\stdClass $course, bool $partialrebuild = false): \stdClass {
|
||||
if (empty($course->id)) {
|
||||
throw new coding_exception('Object $course is missing required property \id\'');
|
||||
}
|
||||
|
||||
$lock = self::get_course_cache_lock($course->id);
|
||||
try {
|
||||
return self::inner_build_course_cache($course, $lock);
|
||||
return self::inner_build_course_cache($course, $lock, $partialrebuild);
|
||||
} finally {
|
||||
$lock->release();
|
||||
}
|
||||
@ -675,9 +694,11 @@ class course_modinfo {
|
||||
*
|
||||
* @param stdClass $course object from DB table course
|
||||
* @param \core\lock\lock $lock Lock object - not actually used, just there to indicate you have a lock
|
||||
* @param bool $partialrebuild Is partial rebuild or not
|
||||
* @return stdClass Course object that has been stored in MUC
|
||||
*/
|
||||
protected static function inner_build_course_cache($course, \core\lock\lock $lock) {
|
||||
protected static function inner_build_course_cache(\stdClass $course, \core\lock\lock $lock,
|
||||
bool $partialrebuild = false): \stdClass {
|
||||
global $DB, $CFG;
|
||||
require_once("{$CFG->dirroot}/course/lib.php");
|
||||
|
||||
@ -693,8 +714,8 @@ class course_modinfo {
|
||||
// This may take time on large courses and it is possible that another user modifies the same course during this process.
|
||||
// Field cacherev stored in both DB and cache will ensure that cached data matches the current course state.
|
||||
$coursemodinfo = new stdClass();
|
||||
$coursemodinfo->modinfo = get_array_of_activities($course->id);
|
||||
$coursemodinfo->sectioncache = self::build_course_section_cache($course);
|
||||
$coursemodinfo->modinfo = get_array_of_activities($course->id, $partialrebuild);
|
||||
$coursemodinfo->sectioncache = self::build_course_section_cache($course, $partialrebuild);
|
||||
foreach (self::$cachedfields as $key) {
|
||||
$coursemodinfo->$key = $course->$key;
|
||||
}
|
||||
@ -2415,9 +2436,22 @@ function get_course_and_cm_from_instance($instanceorid, $modulename, $courseorid
|
||||
* @param int $courseid id of course to rebuild, empty means all
|
||||
* @param boolean $clearonly only clear the cache, gets rebuild automatically on the fly.
|
||||
* Recommended to set to true to avoid unnecessary multiple rebuilding.
|
||||
* @param boolean $partialrebuild will not delete the whole cache when it's true.
|
||||
* use course_purge_module_cache() or course_purge_section_cache() must be
|
||||
* called before when partialrebuild is true.
|
||||
* use course_purge_module_cache() to invalidate mod cache.
|
||||
* use course_purge_section_cache() to invalidate section cache.
|
||||
*
|
||||
* @return void
|
||||
* @throws coding_exception
|
||||
*/
|
||||
function rebuild_course_cache($courseid=0, $clearonly=false) {
|
||||
global $COURSE, $SITE, $DB, $CFG;
|
||||
function rebuild_course_cache(int $courseid = 0, bool $clearonly = false, bool $partialrebuild = false): void {
|
||||
global $COURSE, $SITE, $DB;
|
||||
|
||||
if ($courseid == 0 and !empty($partialrebuild)) {
|
||||
$error = 'partialrebuild only works when a valid course id is provided.';
|
||||
throw new coding_exception($error);
|
||||
}
|
||||
|
||||
// Function rebuild_course_cache() can not be called during upgrade unless it's clear only.
|
||||
if (!$clearonly && !upgrade_ensure_not_running(true)) {
|
||||
@ -2433,7 +2467,10 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
|
||||
if (empty($courseid)) {
|
||||
// Clearing caches for all courses.
|
||||
increment_revision_number('course', 'cacherev', '');
|
||||
$cachecoursemodinfo->purge();
|
||||
if (!$partialrebuild) {
|
||||
$cachecoursemodinfo->purge();
|
||||
}
|
||||
// Clear memory static cache.
|
||||
course_modinfo::clear_instance_cache();
|
||||
// Update global values too.
|
||||
$sitecacherev = $DB->get_field('course', 'cacherev', array('id' => SITEID));
|
||||
@ -2446,7 +2483,11 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
|
||||
} else {
|
||||
// Clearing cache for one course, make sure it is deleted from user request cache as well.
|
||||
increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $courseid));
|
||||
$cachecoursemodinfo->delete($courseid);
|
||||
if (!$partialrebuild) {
|
||||
// Purge all course modinfo.
|
||||
$cachecoursemodinfo->delete($courseid);
|
||||
}
|
||||
// Clear memory static cache.
|
||||
course_modinfo::clear_instance_cache($courseid);
|
||||
// Update global values too.
|
||||
if ($courseid == $COURSE->id || $courseid == $SITE->id) {
|
||||
@ -2471,10 +2512,13 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
|
||||
core_php_time_limit::raise(); // this could take a while! MDL-10954
|
||||
}
|
||||
|
||||
$rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields));
|
||||
$fields = 'id,' . join(',', course_modinfo::$cachedfields);
|
||||
$sort = '';
|
||||
$rs = $DB->get_recordset("course", $select, $sort, $fields);
|
||||
|
||||
// Rebuild cache for each course.
|
||||
foreach ($rs as $course) {
|
||||
course_modinfo::build_course_cache($course);
|
||||
course_modinfo::build_course_cache($course, $partialrebuild);
|
||||
}
|
||||
$rs->close();
|
||||
}
|
||||
|
@ -365,7 +365,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('forum', 'assign', 'page'), array_keys($modinfo->instances));
|
||||
$this->assertEquals(array('assign', 'forum', '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']));
|
||||
|
@ -25,7 +25,8 @@ information provided here is intended especially for developers.
|
||||
* The completion_info function display_help_icon() which returned the 'Your progress' help icon has been deprecated and
|
||||
should no longer be used.
|
||||
* The completion_info function print_help_icon() which has been deprecated since Moodle 2.0 should no longer be used.
|
||||
* @babel/polyfill has been removed in favour of corejs@3
|
||||
* @babel/polyfill has been removed in favour of corejs@3.
|
||||
* A new parameter $partialrebuild has been added to the rebuild_course_cache.
|
||||
* A new parameter $isbulkupdate has been added to the following functions:
|
||||
- grade_category::update()
|
||||
- grade_category::insert()
|
||||
|
@ -713,6 +713,9 @@ function chat_update_chat_times($chatid=0) {
|
||||
$cond = "modulename='chat' AND eventtype = :eventtype AND instance = :chatid AND timestart <> :chattime";
|
||||
$params = ['chattime' => $chat->chattime, 'eventtype' => CHAT_EVENT_TYPE_CHATTIME, 'chatid' => $chat->id];
|
||||
|
||||
$cm = get_coursemodule_from_instance('chat', $chat->id, $chat->course);
|
||||
course_purge_module_cache($cm);
|
||||
|
||||
if ($event->id = $DB->get_field_select('event', 'id', $cond, $params)) {
|
||||
$event->timestart = $chat->chattime;
|
||||
$event->timesort = $chat->chattime;
|
||||
@ -723,7 +726,7 @@ function chat_update_chat_times($chatid=0) {
|
||||
|
||||
$courseids = array_unique($courseids);
|
||||
foreach ($courseids as $courseid) {
|
||||
rebuild_course_cache($courseid, true);
|
||||
rebuild_course_cache($courseid, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2833,14 +2833,23 @@ function lti_update_type($type, $config) {
|
||||
}
|
||||
require_once($CFG->libdir.'/modinfolib.php');
|
||||
if ($clearcache) {
|
||||
$sql = "SELECT DISTINCT course
|
||||
FROM {lti}
|
||||
WHERE typeid = ?";
|
||||
$sql = "SELECT cm.id, cm.course
|
||||
FROM {course_modules} cm
|
||||
JOIN {modules} m ON cm.module = m.id
|
||||
JOIN {lti} l ON l.course = cm.course
|
||||
WHERE m.name = :name AND l.typeid = :typeid";
|
||||
|
||||
$courses = $DB->get_fieldset_sql($sql, array($type->id));
|
||||
$rs = $DB->get_recordset_sql($sql, ['name' => 'lti', 'typeid' => $type->id]);
|
||||
|
||||
foreach ($courses as $courseid) {
|
||||
rebuild_course_cache($courseid, true);
|
||||
$courseids = [];
|
||||
foreach ($rs as $record) {
|
||||
$courseids[] = $record->course;
|
||||
course_purge_module_cache($record);
|
||||
}
|
||||
$rs->close();
|
||||
$courseids = array_unique($courseids);
|
||||
foreach ($courseids as $courseid) {
|
||||
rebuild_course_cache($courseid, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user