From 379eaebde119fd86824fb09c4b9849463f483ef1 Mon Sep 17 00:00:00 2001 From: skodak Date: Tue, 14 Aug 2007 20:35:04 +0000 Subject: [PATCH] MDL-10383 - backup/restore of new groups&groupings finished --- backup/backuplib.php | 19 +- backup/restorelib.php | 431 ++++++++++++++++++++++++---------------- lang/en_utf8/moodle.php | 1 + 3 files changed, 275 insertions(+), 176 deletions(-) diff --git a/backup/backuplib.php b/backup/backuplib.php index f6e04ee1a9..3eec8a330b 100644 --- a/backup/backuplib.php +++ b/backup/backuplib.php @@ -2053,27 +2053,28 @@ //Get grouping_groups $courseid = $preferences->backup_course; - $sql = "SELECT gg.* FROM {$CFG->prefix}groupings g, {$CFG->prefix}groupings_groups g + $sql = "SELECT gg.* FROM {$CFG->prefix}groupings g, {$CFG->prefix}groupings_groups gg WHERE g.courseid=$courseid AND g.id=gg.groupingid"; $grouping_groups = get_records_sql($sql); //Pring grouping_groups header if ($grouping_groups) { //Pring grouping_groups header - fwrite ($bf,start_tag("GROUPINGSGROUPS",4,true)); + fwrite ($bf,start_tag("GROUPINGSGROUPS",2,true)); //Iterate - foreach ($grouping_groups as $group2) { + foreach ($grouping_groups as $members) { //Begin grouping_group tag - fwrite ($bf,start_tag("GROUPINGSGROUP",5,true)); + fwrite ($bf,start_tag("GROUPINGGROUP",3,true)); //Output group_member contents - fwrite ($bf,full_tag("GROUPINGID",6,false,$group2->groupingid)); - fwrite ($bf,full_tag("GROUPID",6,false,$group2->groupid)); - fwrite ($bf,full_tag("TIMEADDED",6,false,$group2->timeadded)); + fwrite ($bf,full_tag("ID",4,false,$members->id)); + fwrite ($bf,full_tag("GROUPINGID",4,false,$members->groupingid)); + fwrite ($bf,full_tag("GROUPID",4,false,$members->groupid)); + fwrite ($bf,full_tag("TIMEADDED",4,false,$members->timeadded)); //End grouping_group tag - fwrite ($bf,end_tag("GROUPINGSGROUP",5,true)); + fwrite ($bf,end_tag("GROUPINGGROUP",3,true)); } //End grouping_groups tag - $status = fwrite ($bf,end_tag("GROUPINGSGROUPS",4,true)); + $status = fwrite ($bf,end_tag("GROUPINGSGROUPS",2,true)); } return $status; } diff --git a/backup/restorelib.php b/backup/restorelib.php index e9f5b967b1..9d6882fd57 100644 --- a/backup/restorelib.php +++ b/backup/restorelib.php @@ -597,6 +597,7 @@ //Create the course_object if ($status) { + $course = new object(); $course->category = addslashes($course_header->category->id); $course->password = addslashes($course_header->course_password); $course->fullname = addslashes($course_header->course_fullname); @@ -624,7 +625,10 @@ if (isset($course_header->course_groupmodeforce)) { $course->groupmodeforce = addslashes($course_header->course_groupmodeforce); } - $course->defaultgroupingid = 0; // TODO: add proper handling of ddefaultgroupingid + if (isset($course_header->course_defaultgroupingid)) { + //keep the original now - convert after groupings restored + $course->defaultgroupingid = addslashes($course_header->course_defaultgroupingid); + } $course->lang = addslashes($course_header->course_lang); $course->theme = addslashes($course_header->course_theme); $course->cost = addslashes($course_header->course_cost); @@ -965,6 +969,7 @@ //Get the module id from modules $module = get_record("modules","name",$mod->type); if ($module) { + $coursemodule = new object(); $course_module->course = $restore->course_id; $course_module->module = $module->id; $course_module->section = $newid; @@ -972,9 +977,9 @@ $course_module->score = $mod->score; $course_module->indent = $mod->indent; $course_module->visible = $mod->visible; - if (isset($mod->groupmode)) { - $course_module->groupmode = $mod->groupmode; - } + $course_module->groupmode = $mod->groupmode; + $course_module->groupingid = $mod->groupingid; + $course_module->groupmembersonly = $mod->groupmembersonly; $course_module->instance = 0; //NOTE: The instance (new) is calculated and updated in db in the // final step of the restore. We don't know it yet. @@ -2705,6 +2710,7 @@ //$GLOBALS['traverse_array']=""; //Debug //Now build the SCALE record structure + $sca = new object(); $sca->courseid = backup_todb($info['SCALE']['#']['COURSEID']['0']['#']); $sca->userid = backup_todb($info['SCALE']['#']['USERID']['0']['#']); $sca->name = backup_todb($info['SCALE']['#']['NAME']['0']['#']); @@ -2767,97 +2773,97 @@ //This function creates all the groups function restore_create_groups($restore,$xml_file) { - global $CFG, $db; - - $status = true; - $status2 = true; //Check it exists if (!file_exists($xml_file)) { - $status = false; + return false; } //Get info from xml - if ($status) { + if (!$groups = restore_read_xml_groups($restore,$xml_file)) { //groups will contain the old_id of every group //in backup_ids->info will be the real info (serialized) - $groups = restore_read_xml_groups($restore,$xml_file); - } - //Now, if we have anything in groups, we have to restore that - //groups - if ($groups) { - if ($groups !== true) { - //Iterate over each group - foreach ($groups as $group) { - //Get record from backup_ids - $data = backup_getid($restore->backup_unique_code,"groups",$group->id); + return false; - if ($data) { - //Now get completed xmlized object - $info = $data->info; - //traverse_xmlize($info); //Debug - //print_object ($GLOBALS['traverse_array']); //Debug - //$GLOBALS['traverse_array']=""; //Debug - //Now build the GROUP record structure - $gro = new Object(); - $gro->courseid = $restore->course_id; - $gro->name = backup_todb($info['GROUP']['#']['NAME']['0']['#']); - $gro->description = backup_todb($info['GROUP']['#']['DESCRIPTION']['0']['#']); - if (isset($info['GROUP']['#']['ENROLMENTKEY']['0']['#'])) { - $gro->enrolmentkey = backup_todb($info['GROUP']['#']['ENROLMENTKEY']['0']['#']); - } else { - $gro->enrolmentkey = backup_todb($info['GROUP']['#']['PASSWORD']['0']['#']); - } - $gro->picture = backup_todb($info['GROUP']['#']['PICTURE']['0']['#']); - $gro->hidepicture = backup_todb($info['GROUP']['#']['HIDEPICTURE']['0']['#']); - $gro->timecreated = backup_todb($info['GROUP']['#']['TIMECREATED']['0']['#']); - $gro->timemodified = backup_todb($info['GROUP']['#']['TIMEMODIFIED']['0']['#']); + } else if ($groups === true) { + return true; + } - //Now search if that group exists (by name and description field) in - if (!$gro_db = get_record('groups', 'courseid', $restore->course_id, 'name', $gro->name, 'description', $gro->description)) { - //If it doesn't exist, create - $newid = insert_record ('groups', $gro); + $status = true; - } else { - //get current group id - $newid = $gro_db->id; - } + //Iterate over each group + foreach ($groups as $group) { + //Get record from backup_ids + $data = backup_getid($restore->backup_unique_code,"groups",$group->id); + + if ($data) { + //Now get completed xmlized object + $info = $data->info; + //traverse_xmlize($info); //Debug + //print_object ($GLOBALS['traverse_array']); //Debug + //$GLOBALS['traverse_array']=""; //Debug + //Now build the GROUP record structure + $gro = new Object(); + $gro->courseid = $restore->course_id; + $gro->name = backup_todb($info['GROUP']['#']['NAME']['0']['#']); + $gro->description = backup_todb($info['GROUP']['#']['DESCRIPTION']['0']['#']); + if (isset($info['GROUP']['#']['ENROLMENTKEY']['0']['#'])) { + $gro->enrolmentkey = backup_todb($info['GROUP']['#']['ENROLMENTKEY']['0']['#']); + } else { + $gro->enrolmentkey = backup_todb($info['GROUP']['#']['PASSWORD']['0']['#']); + } + $gro->picture = backup_todb($info['GROUP']['#']['PICTURE']['0']['#']); + $gro->hidepicture = backup_todb($info['GROUP']['#']['HIDEPICTURE']['0']['#']); + $gro->timecreated = backup_todb($info['GROUP']['#']['TIMECREATED']['0']['#']); + $gro->timemodified = backup_todb($info['GROUP']['#']['TIMEMODIFIED']['0']['#']); - if ($newid) { - //We have the newid, update backup_ids - backup_putid($restore->backup_unique_code,"groups", $group->id, $newid); - } + //Now search if that group exists (by name and description field) in + if (!$gro_db = get_record('groups', 'courseid', $restore->course_id, 'name', $gro->name, 'description', $gro->description)) { + //If it doesn't exist, create + $newid = insert_record('groups', $gro); - //Now restore members in the groups_members, only if - //users are included - if ($restore->users != 2) { - $status2 = restore_create_groups_members($newid,$info,$restore); - } - } + } else { + //get current group id + $newid = $gro_db->id; + } + + if ($newid) { + //We have the newid, update backup_ids + backup_putid($restore->backup_unique_code,"groups", $group->id, $newid); + } else { + + $status = false; + continue; } - //Now, restore group_files - if ($status && $status2) { - $status2 = restore_group_files($restore); + + //Now restore members in the groups_members, only if + //users are included + if ($restore->users != 2) { + if (!restore_create_groups_members($newid,$info,$restore)) { + $status = false; + } } } - } else { - $status = false; } - return ($status && $status2); + + //Now, restore group_files + if ($status) { + $status = restore_group_files($restore); + } + + return $status; } //This function restores the groups_members function restore_create_groups_members($group_id,$info,$restore) { - global $CFG; - - $status = true; - if (! isset($info['GROUP']['#']['MEMBERS']['0']['#']['MEMBER'])) { //OK, some groups have no members. - return $status; + return true; } //Get the members array $members = $info['GROUP']['#']['MEMBERS']['0']['#']['MEMBER']; + $status = true; + //Iterate over members for($i = 0; $i < sizeof($members); $i++) { $mem_info = $members[$i]; @@ -2874,14 +2880,17 @@ $newid = false; //We have to recode the userid field - $user = backup_getid($restore->backup_unique_code,"user",$group_member->userid); - if ($user) { - $group_member->userid = $user->new_id; + if (!$user = backup_getid($restore->backup_unique_code,"user",$group_member->userid)) { + $status = false; + continue; + } - //The structure is equal to the db, so insert the groups_members - $newid = insert_record ("groups_members", $group_member); - } else { - echo "grrr"; + $group_member->userid = $user->new_id; + + //The structure is equal to the db, so insert the groups_members + if (!insert_record ("groups_members", $group_member)) { + $status = false; + continue; } //Do some output @@ -2894,10 +2903,6 @@ } backup_flush(300); } - - if (!$newid) { - $status = false; - } } return $status; @@ -2906,112 +2911,112 @@ //This function creates all the groupings function restore_create_groupings($restore,$xml_file) { - global $CFG, $db; - - $status = true; - //Check it exists if (!file_exists($xml_file)) { - $status = false; + return false; } //Get info from xml - if ($status) { - //groupings will contain the old_id of every group - //in backup_ids->info will be the real info (serialized) - $groupings = restore_read_xml_groupings($restore,$xml_file); + if (!$groupings = restore_read_xml_groupings($restore,$xml_file)) { + return false; + + } else if ($groupings === true) { + return true; } - //Now, if we have anything in groupings, we have to restore that grouping - if ($groupings) { - if ($groupings !== true) { - //Iterate over each group - foreach ($groupings as $grouping) { - //Get record from backup_ids - $data = backup_getid($restore->backup_unique_code,"groupings",$grouping->id); - //Init variables - $create_grouping = false; - if ($data) { - //Now get completed xmlized object - $info = $data->info; - //Now build the GROUPING record structure - $gro = new Object(); - ///$gro->id = backup_todb($info['GROUPING']['#']['ID']['0']['#']); - $gro->courseid = $restore->course_id; - $gro->name = backup_todb($info['GROUPING']['#']['NAME']['0']['#']); - $gro->description = backup_todb($info['GROUPING']['#']['DESCRIPTION']['0']['#']); - $gro->configdata = backup_todb($info['GROUPING']['#']['CONFIGDATA']['0']['#']); - $gro->timecreated = backup_todb($info['GROUPING']['#']['TIMECREATED']['0']['#']); - - //Now search if that group exists (by name and description field) in - if (!$gro_db = get_record('groupings', 'courseid', $restore->course_id, 'name', $gro->name, 'description', $gro->description)) { - //The structure is equal to the db, so insert the grouping - $newid = insert_record($restore->course_id, $gro); + $status = true; - } else { - //get current group id - $newid = $gro_db->id; - } + //Iterate over each group + foreach ($groupings as $grouping) { + if ($data = backup_getid($restore->backup_unique_code,"groupings",$grouping->id)) { + //Now get completed xmlized object + $info = $data->info; + //Now build the GROUPING record structure + $gro = new Object(); + ///$gro->id = backup_todb($info['GROUPING']['#']['ID']['0']['#']); + $gro->courseid = $restore->course_id; + $gro->name = backup_todb($info['GROUPING']['#']['NAME']['0']['#']); + $gro->description = backup_todb($info['GROUPING']['#']['DESCRIPTION']['0']['#']); + $gro->configdata = backup_todb($info['GROUPING']['#']['CONFIGDATA']['0']['#']); + $gro->timecreated = backup_todb($info['GROUPING']['#']['TIMECREATED']['0']['#']); + + //Now search if that group exists (by name and description field) in + if ($gro_db = get_record('groupings', 'courseid', $restore->course_id, 'name', $gro->name, 'description', $gro->description)) { + //get current group id + $newid = $gro_db->id; - if ($newid) { - //We have the newid, update backup_ids - backup_putid($restore->backup_unique_code,"groupings", - $grouping->id, $newid); - } + } else { + //The structure is equal to the db, so insert the grouping + if (!$newid = insert_record('groupings', $gro)) { + $status = false; + continue; } } - //(Now, restore grouping_files) + + //We have the newid, update backup_ids + backup_putid($restore->backup_unique_code,"groupings", + $grouping->id, $newid); + } + } + + + // now fix the defaultgroupingid in course + $course = get_record('course', 'id', $restore->course_id); + if ($course->defaultgroupingid) { + if ($grouping = backup_getid($restore->backup_unique_code,"groupings",$course->defaultgroupingid)) { + set_field('course', 'defaultgroupingid', $grouping->new_id, 'id', $course->id); + } else { + set_field('course', 'defaultgroupingid', 0, 'id', $course->id); } - } else { - $status = false; } return $status; } - //This function restores the groups_members + //This function creates all the groupingsgroups function restore_create_groupings_groups($restore,$xml_file) { -//TODO: fix me -return true; + //Check it exists + if (!file_exists($xml_file)) { + return false; + } + //Get info from xml + if (!$groupingsgroups = restore_read_xml_groupings_groups($restore,$xml_file)) { + return false; - global $CFG; + } else if ($groupingsgroups === true) { + return true; + } $status = true; - //Get the members array - $members = $info['GROUPING']['#']['GROUPS']['0']['#']['GROUP']; - - //Iterate over members - for($i = 0; $i < sizeof($members); $i++) { - $mem_info = $members[$i]; - //Now, build the GROUPINGS_GROUPS record structure - $gro_member = new Object(); - $gro_member->groupingid = $grouping_id; - $gro_member->groupid = backup_todb($mem_info['#']['GROUPID']['0']['#']); - $gro_member->timeadded = backup_todb($mem_info['#']['TIMEADDED']['0']['#']); + //Iterate over each group + foreach ($groupingsgroups as $groupinggroup) { + if ($data = backup_getid($restore->backup_unique_code,"groupingsgroups",$groupinggroup->id)) { + //Now get completed xmlized object + $info = $data->info; + //Now build the GROUPING record structure + $gro_member = new Object(); + $gro_member->groupingid = backup_todb($info['GROUPINGGROUP']['#']['GROUPINGID']['0']['#']); + $gro_member->groupid = backup_todb($info['GROUPINGGROUP']['#']['GROUPID']['0']['#']); + $gro_member->timeadded = backup_todb($info['GROUPINGGROUP']['#']['TIMEADDED']['0']['#']); + + if (!$grouping = backup_getid($restore->backup_unique_code,"groupings",$gro_member->groupingid)) { + $status = false; + continue; + } - //We have to recode the userid field - ///$user = backup_getid($restore->backup_unique_code,"user",$group_member->userid); - $group = backup_getid($restore->backup_unique_code,"group",$gro_member->groupid); - if ($group) { - $gro_member->groupid = $group->new_id; - } + if (!$group = backup_getid($restore->backup_unique_code,"groups",$gro_member->groupid)) { + $status = false; + continue; + } - //The structure is equal to the db, so link the groups to the groupings. TODO: RESTORE. - $newid = groups_add_group_to_grouping($gro_member->groupid, $gro_member->groupingid); - //Do some output - if (($i+1) % 50 == 0) { - if (!defined('RESTORE_SILENTLY')) { - echo "."; - if (($i+1) % 1000 == 0) { - echo "
"; + $gro_member->groupid = $group->new_id; + $gro_member->groupingid = $grouping->new_id; + if (!get_record('groupings_groups', 'groupid', $gro_member->groupid, 'groupingid', $gro_member->groupingid)) { + if (!insert_record('groupings_groups', $gro_member)) { + $status = false; } } - backup_flush(300); - } - - if (!$newid) { - $status = false; } } @@ -3161,9 +3166,9 @@ return true; // I noticed some parts of the restore code is calling this directly instead of calling backup_todb(), so just in case // 3rd party mod etc are doing the same if ($content === NULL) { - return NULL; + return NULL; } - + //Now decode wwwroot and file.php calls $search = array ("$@FILEPHP@$"); @@ -4171,7 +4176,7 @@ return true; } } - function startElementGroupings($parser, $tagName, $attrs) { //TODO: + function startElementGroupings($parser, $tagName, $attrs) { //Refresh properties $this->level++; $this->tree[$this->level] = $tagName; @@ -4198,6 +4203,33 @@ return true; } } + function startElementGroupingsGroups($parser, $tagName, $attrs) { + //Refresh properties + $this->level++; + $this->tree[$this->level] = $tagName; + + //if ($tagName == "GROUPINGGROUP" && $this->tree[3] == "GROUPINGSGROUPS") { //Debug + // echo "

GROUPINGSGROUP: ".strftime ("%X",time()),"-"; //Debug + //} //Debug + + //Output something to avoid browser timeouts... + backup_flush(); + + //Check if we are into GROUPINGSGROUPS zone + //if ($this->tree[3] == "GROUPINGSGROUPS") //Debug + // echo $this->level.str_repeat(" ",$this->level*2)."<".$tagName.">
\n"; //Debug + + //If we are under a GROUPINGGROUP tag under a GROUPINGSGROUPS zone, accumule it + if (isset($this->tree[4]) and isset($this->tree[3])) { + if (($this->tree[4] == "GROUPINGGROUP") and ($this->tree[3] == "GROUPINGSGROUPS")) { + if (!isset($this->temp)) { + $this->temp = ""; + } + $this->temp .= "<".$tagName.">"; + } + } + } + //This is the startTag handler we use where we are reading the events zone (todo="EVENTS") function startElementEvents($parser, $tagName, $attrs) { //Refresh properties @@ -4535,6 +4567,9 @@ return true; case "GROUPMODEFORCE": $this->info->course_groupmodeforce = $this->getContents(); break; + case "DEFAULTGROUPINGID": + $this->info->course_defaultgroupingid = $this->getContents(); + break; case "LANG": $this->info->course_lang = $this->getContents(); break; @@ -4889,6 +4924,16 @@ return true; if ($this->level == 6) { switch ($tagName) { case "MOD": + if (isset($this->info->tempmod->groupmode)) { + $this->info->tempmod->groupmode = 0; + } + if (isset($this->info->tempmod->groupingid)) { + $this->info->tempmod->groupingid = 0; + } + if (isset($this->info->tempmod->groupmembersonly)) { + $this->info->tempmod->groupmembersonly = 0; + } + //We've finalized a mod, get it $this->info->tempsection->mods[$this->info->tempmod->id]->type = $this->info->tempmod->type; @@ -4904,6 +4949,11 @@ return true; $this->info->tempmod->visible; $this->info->tempsection->mods[$this->info->tempmod->id]->groupmode = $this->info->tempmod->groupmode; + $this->info->tempsection->mods[$this->info->tempmod->id]->groupingid = + $this->info->tempmod->groupingid; + $this->info->tempsection->mods[$this->info->tempmod->id]->groupmembersonly = + $this->info->tempmod->groupmembersonly; + unset($this->info->tempmod); } } @@ -5976,9 +6026,9 @@ return true; } - //This is the endTag handler we use where we are reading the groups zone (todo="GROUPINGS") + //This is the endTag handler we use where we are reading the groupings zone (todo="GROUPINGS") function endElementGroupings($parser, $tagName) { //TODO: - //Check if we are into GROUPS zone + //Check if we are into GROUPINGS zone if ($this->tree[3] == "GROUPINGS") { //Acumulate data to info (content + close tag) //Reconvert: strip htmlchars again and trim to generate xml data @@ -6020,6 +6070,50 @@ return true; } + //This is the endTag handler we use where we are reading the groupingsgroups zone (todo="GROUPINGGROUPS") + function endElementGroupingsGroups($parser, $tagName) { //TODO: + //Check if we are into GROUPINGSGROUPS zone + if ($this->tree[3] == "GROUPINGSGROUPS") { + //Acumulate data to info (content + close tag) + //Reconvert: strip htmlchars again and trim to generate xml data + if (!isset($this->temp)) { + $this->temp = ""; + } + $this->temp .= htmlspecialchars(trim($this->content)).""; + //If we've finished a group, xmlize it an save to db + if (($this->level == 4) and ($tagName == "GROUPINGGROUP")) { + //Prepend XML standard header to info gathered + $xml_data = "\n".$this->temp; + //Call to xmlize for this portion of xml data (one GROUPING) + $data = xmlize($xml_data,0); + //Now, save data to db. We'll use it later + //Get id and from data + $groupinggroup_id = $data["GROUPINGGROUP"]["#"]["ID"]["0"]["#"]; + //Save to db + $status = backup_putid($this->preferences->backup_unique_code,"groupingsgroups",$groupinggroup_id, + null,$data); + //Create returning info + $ret_info = new Object(); + $ret_info->id = $groupinggroup_id; + $this->info[] = $ret_info; + //Reset temp + unset($this->temp); + } + } + + //Stop parsing if todo = GROUPINGS and tagName = GROUPING (en of the tag, of course) + //Speed up a lot (avoid parse all) + if ($tagName == "GROUPINGSGROUPS" and $this->level == 3) { + $this->finished = true; + } + + //Clear things + $this->tree[$this->level] = ""; + $this->level--; + $this->content = ""; + + } + //This is the endTag handler we use where we are reading the events zone (todo="EVENTS") function endElementEvents($parser, $tagName) { //Check if we are into EVENTS zone @@ -6264,6 +6358,9 @@ return true; } else if ($todo == "GROUPINGS") { //Define handlers to that zone xml_set_element_handler($xml_parser, "startElementGroupings", "endElementGroupings"); + } else if ($todo == "GROUPINGSGROUPS") { + //Define handlers to that zone + xml_set_element_handler($xml_parser, "startElementGroupingsGroups", "endElementGroupingsGroups"); } else if ($todo == "EVENTS") { //Define handlers to that zone xml_set_element_handler($xml_parser, "startElementEvents", "endElementEvents"); @@ -6999,9 +7096,9 @@ return true; } if (!$status = restore_create_groupings_groups($restore,$xml_file)) { if (!defined('RESTORE_SILENTLY')) { - notify("Could not restore groupings groups!"); + notify("Could not restore groups in groupings!"); } else { - $errorstr = "Could not restore groupings groups!"; + $errorstr = "Could not restore groups in groupings!"; return false; } } diff --git a/lang/en_utf8/moodle.php b/lang/en_utf8/moodle.php index 28acde923d..884646e982 100644 --- a/lang/en_utf8/moodle.php +++ b/lang/en_utf8/moodle.php @@ -305,6 +305,7 @@ $string['creatingevents'] = 'Creating events'; $string['creatinggradebook'] = 'Creating gradebook'; $string['creatinggroups'] = 'Creating groups'; $string['creatinggroupings'] = 'Creating groupings'; +$string['creatinggroupingsgroups'] = 'Adding groups into groupings'; $string['creatinglogentries'] = 'Creating log entries'; $string['creatingmessagesinfo'] = 'Creating messages info'; $string['creatingmetacoursedata'] = 'Creating metacourse info'; -- 2.39.5