From 746f1ac85ccf60867924e36a6deaa9e2b61f9ef7 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Wed, 29 Jun 2011 15:26:48 +0800 Subject: [PATCH] MDL-27988 backup grade: Calculated grade items calculation references now corrected and idnumber restore improved --- backup/moodle2/restore_stepslib.php | 97 ++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index c8594923e3905..6f41077f0d96a 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -273,7 +273,9 @@ protected function process_grade_setting($data) { //$this->set_mapping('grade_setting', $oldid, $newitemid); } - //put all activity grade items in the correct grade category and mark all for recalculation + /** + * put all activity grade items in the correct grade category and mark all for recalculation + */ protected function after_execute() { global $DB; @@ -284,8 +286,15 @@ protected function after_execute() { ); $rs = $DB->get_recordset('backup_ids_temp', $conditions); + // We need this for calculation magic later on. + $mappings = array(); + if (!empty($rs)) { foreach($rs as $grade_item_backup) { + + // Store the oldid with the new id. + $mappings[$grade_item_backup->itemid] = $grade_item_backup->newitemid; + $updateobj = new stdclass(); $updateobj->id = $grade_item_backup->newitemid; @@ -301,6 +310,55 @@ protected function after_execute() { } $rs->close(); + // We need to update the calculations for calculated grade items that may reference old + // grade item ids using ##gi\d+##. + list($sql, $params) = $DB->get_in_or_equal(array_values($mappings), SQL_PARAMS_NAMED); + $sql = "SELECT gi.id, gi.calculation + FROM {grade_items} gi + WHERE gi.id {$sql} AND + calculation IS NOT NULL"; + $rs = $DB->get_recordset_sql($sql, $params); + foreach ($rs as $gradeitem) { + // Collect all of the used grade item id references + if (preg_match_all('/##gi(\d+)##/', $gradeitem->calculation, $matches) < 1) { + // This calculation doesn't reference any other grade items... EASY! + continue; + } + // For this next bit we are going to do the replacement of id's in two steps: + // 1. We will replace all old id references with a special mapping reference. + // 2. We will replace all mapping references with id's + // Why do we do this? + // Because there potentially there will be an overlap of ids within the query and we + // we substitute the wrong id.. safest way around this is the two step system + $calculationmap = array(); + $mapcount = 0; + foreach ($matches[1] as $match) { + // Check that the old id is known to us, if not it was broken to begin with and will + // continue to be broken. + if (!array_key_exists($match, $mappings)) { + continue; + } + // Our special mapping key + $mapping = '##MAPPING'.$mapcount.'##'; + // The old id that exists within the calculation now + $oldid = '##gi'.$match.'##'; + // The new id that we want to replace the old one with. + $newid = '##gi'.$mappings[$match].'##'; + // Replace in the special mapping key + $gradeitem->calculation = str_replace($oldid, $mapping, $gradeitem->calculation); + // And record the mapping + $calculationmap[$mapping] = $newid; + $mapcount++; + } + // Iterate all special mappings for this calculation and replace in the new id's + foreach ($calculationmap as $mapping => $newid) { + $gradeitem->calculation = str_replace($mapping, $newid, $gradeitem->calculation); + } + // Update the calculation now that its being remapped + $DB->update_record('grade_items', $gradeitem); + } + $rs->close(); + //need to correct the grade category path and parent $conditions = array( 'courseid' => $this->get_courseid() @@ -1800,28 +1858,47 @@ protected function define_structure() { } protected function process_grade_item($data) { + global $DB; $data = (object)($data); $oldid = $data->id; // We'll need these later $oldparentid = $data->categoryid; + $courseid = $this->get_courseid(); // make sure top course category exists, all grade items will be associated // to it. Later, if restoring the whole gradebook, categories will be introduced - $coursecat = grade_category::fetch_course_category($this->get_courseid()); + $coursecat = grade_category::fetch_course_category($courseid); $coursecatid = $coursecat->id; // Get the categoryid to be used + $idnumber = null; + if (!empty($data->idnumber)) { + // Don't get any idnumber from course module. Keep them as they are in grade_item->idnumber + // Reason: it's not clear what happens with outcomes->idnumber or activities with multiple items (workshop) + // so the best is to keep the ones already in the gradebook + // Potential problem: duplicates if same items are restored more than once. :-( + // This needs to be fixed in some way (outcomes & activities with multiple items) + // $data->idnumber = get_coursemodule_from_instance($data->itemmodule, $data->iteminstance)->idnumber; + // In any case, verify always for uniqueness + $sql = "SELECT cm.id + FROM {course_modules} cm + WHERE cm.course = :courseid AND + cm.idnumber = :idnumber AND + cm.id <> :cmid"; + $params = array( + 'courseid' => $courseid, + 'idnumber' => $data->idnumber, + 'cmid' => $this->task->get_moduleid() + ); + if (!$DB->record_exists_sql($sql, $params) && !$DB->record_exists('grade_items', array('courseid' => $courseid, 'idnumber' => $data->idnumber))) { + $idnumber = $data->idnumber; + } + } + unset($data->id); $data->categoryid = $coursecatid; $data->courseid = $this->get_courseid(); $data->iteminstance = $this->task->get_activityid(); - // Don't get any idnumber from course module. Keep them as they are in grade_item->idnumber - // Reason: it's not clear what happens with outcomes->idnumber or activities with multiple items (workshop) - // so the best is to keep the ones already in the gradebook - // Potential problem: duplicates if same items are restored more than once. :-( - // This needs to be fixed in some way (outcomes & activities with multiple items) - // $data->idnumber = get_coursemodule_from_instance($data->itemmodule, $data->iteminstance)->idnumber; - // In any case, verify always for uniqueness - $data->idnumber = grade_verify_idnumber($data->idnumber, $this->get_courseid()) ? $data->idnumber : null; + $data->idnumber = $idnumber; $data->scaleid = $this->get_mappingid('scale', $data->scaleid); $data->outcomeid = $this->get_mappingid('outcome', $data->outcomeid); $data->timecreated = $this->apply_date_offset($data->timecreated);