MDL-8270 full block backup/restore - based on patches by Mark Nielsen and code in HEAD by Tim Hunt

This commit is contained in:
skodak
2008-03-23 15:50:08 +00:00
parent 90165e2d01
commit a18258a969
4 changed files with 307 additions and 79 deletions
+56 -27
View File
@@ -929,27 +929,31 @@
$pages = array();
$pages[] = page_create_object(PAGE_COURSE_VIEW, $preferences->backup_course);
// Let's see if we have to backup blocks from modules
$modulerecords = get_records_sql('SELECT name, id FROM '.$CFG->prefix.'modules');
if (!empty($CFG->showblocksonmodpages)) {
// get course structure
$course = get_record('course', 'id', $preferences->backup_course);
$modinfo =& get_fast_modinfo($course);
foreach($preferences->mods as $module) {
if(!$module->backup) {
continue;
}
foreach($preferences->mods as $module) {
if (!$module->backup) {
continue;
}
$cmods = get_records_select('course_modules', 'course = '.$preferences->backup_course.' AND module = '.$modulerecords[$module->name]->id);
if(empty($cmods)) {
continue;
}
if (empty($modinfo->instances[$module->name])) {
continue;
}
$pagetypes = page_import_types('mod/'.$module->name.'/');
if(empty($pagetypes)) {
continue;
}
$pagetypes = page_import_types('mod/'.$module->name.'/');
if (empty($pagetypes)) {
continue;
}
foreach($pagetypes as $pagetype) {
foreach($cmods as $cmod) {
$pages[] = page_create_object($pagetype, $cmod->instance);
foreach($pagetypes as $pagetype) {
foreach($modinfo->instances[$module->name] as $cm) {
if (!empty($module->instances[$cm->instance]->backup)) {
$pages[] = page_create_object($pagetype, $cm->instance);
}
}
}
}
}
@@ -957,7 +961,7 @@
//Blocks open tag
fwrite ($bf,start_tag('BLOCKS',2,true));
while($page = array_pop($pages)) {
foreach($pages as $page) {
if ($instances = blocks_get_by_page($page)) {
//Iterate over every block
foreach ($instances as $position) {
@@ -967,26 +971,32 @@
if(empty($blocks[$instance->blockid]->name)) {
continue;
}
$blockname = $blocks[$instance->blockid]->name;
//Give the block a chance to process any links in configdata.
if (!isset($blocks[$instance->blockid]->blockobject)) {
$blocks[$instance->blockid]->blockobject = block_instance($blocks[$instance->blockid]->name);
if (!$blockobj = block_instance($blockname, $instance)) {
// Invalid block
continue;
}
$config = unserialize(base64_decode($instance->configdata));
$blocks[$instance->blockid]->blockobject->backup_encode_absolute_links_in_config($config);
$instance->configdata = base64_encode(serialize($config));
// encode absolute links in block config
$instance->configdata = $blockobj->get_backup_encoded_config();
//Begin Block
fwrite ($bf,start_tag('BLOCK',3,true));
fwrite ($bf,full_tag('ID', 4, false,$instance->id));
fwrite ($bf,full_tag('NAME',4,false,$blocks[$instance->blockid]->name));
fwrite ($bf,full_tag('NAME',4,false,$blockname));
fwrite ($bf,full_tag('PAGEID',4,false,$instance->pageid));
fwrite ($bf,full_tag('PAGETYPE',4,false,$instance->pagetype));
fwrite ($bf,full_tag('POSITION',4,false,$instance->position));
fwrite ($bf,full_tag('WEIGHT',4,false,$instance->weight));
fwrite ($bf,full_tag('VISIBLE',4,false,$instance->visible));
fwrite ($bf,full_tag('CONFIGDATA',4,false,$instance->configdata));
// Write instance data if needed
if ($blockobj->backuprestore_instancedata_used()) {
fwrite ($bf,start_tag('INSTANCEDATA',4,true));
$status = $blockobj->instance_backup($bf, $preferences);
fwrite ($bf,end_tag('INSTANCEDATA',4,true));
}
$context = get_context_instance(CONTEXT_BLOCK, $instance->id);
write_role_overrides_xml($bf, $context, 4);
/// write role_assign code here
@@ -1484,7 +1494,7 @@
JOIN {$CFG->prefix}grade_items gi
ON (gi.iteminstance = gc.id)
WHERE gc.courseid = $preferences->backup_course
AND (gi.itemtype='course' OR gi.itemtype='category')
AND (gi.itemtype='course' OR gi.itemtype='category')
ORDER BY gi.sortorder ASC");
if ($grade_categories) {
@@ -2227,6 +2237,25 @@
}
}
// For each block, call its encode_content_links method.
// This encodes forexample links to blocks/something/viewphp?id=666
// that are stored in other activities.
static $blockobjects = null;
if (!isset($blockobjects)) {
$blockobjects = array();
if ($blocks = get_records('block', 'visible', 1)) {
foreach ($blocks as $block) {
if ($blockobject = block_instance($block->name)) {
$blockobjects[] = $blockobject;
}
}
}
}
foreach ($blockobjects as $blockobject) {
$result = $blockobject->encode_content_links($result,$mypreferences);
}
if ($result != $content) {
debugging('<br /><hr />'.s($content).'<br />changed to<br />'.s($result).'<hr /><br />');
}
+118 -33
View File
@@ -104,7 +104,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string ("from")." ".get_string("modulenameplural",$name);
}
$status = $function_name($restore);
$status = $function_name($restore) && $status;
if (!defined('RESTORE_SILENTLY')) {
echo '</li>';
}
@@ -114,22 +114,16 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
// Process all html text also in blocks too
if (!defined('RESTORE_SILENTLY')) {
echo '<li>' . get_string ('from') . ' ' . get_string('blocks');
echo '<li>'.get_string ('from').' '.get_string('blocks');
}
if (!empty($restore->blockinstanceids)) {
$blocks = blocks_get_record();
$instances = get_records_list('block_instance', 'id', implode(',', $restore->blockinstanceids), '', 'id,blockid,configdata');
foreach ($instances as $instance) {
if (!isset($blocks[$instance->blockid]->blockobject)) {
$blocks[$instance->blockid]->blockobject = block_instance($blocks[$instance->blockid]->name);
}
$config = unserialize(base64_decode($instance->configdata));
if ($blocks[$instance->blockid]->blockobject->restore_decode_absolute_links_in_config($config)) {
$instance->configdata = base64_encode(serialize($config));
$status = $status && update_record('block_instance', $instance);
}
if ($blocks = get_records('block', 'visible', 1)) {
foreach ($blocks as $block) {
$blockobject = block_instance($block->name);
$blockobject->decode_content_links_caller($restore);
}
}
if (!defined('RESTORE_SILENTLY')) {
echo '</li>';
}
@@ -139,7 +133,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
if (!defined('RESTORE_SILENTLY')) {
echo '<li>' . get_string('from') . ' ' . get_string('questions', 'quiz');
}
$status = question_decode_content_links_caller($restore);
$status = question_decode_content_links_caller($restore) && $status;
if (!defined('RESTORE_SILENTLY')) {
echo '</li>';
}
@@ -161,6 +155,24 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
$content = $function_name($content,$restore);
}
}
// For each block, call its encode_content_links method
static $blockobjects = null;
if (!isset($blockobjects)) {
$blockobjects = array();
if ($blocks = get_records('block', 'visible', 1)) {
foreach ($blocks as $block) {
if ($blockobject = block_instance($block->name)) {
$blockobjects[] = $blockobject;
}
}
}
}
foreach ($blockobjects as $blockobject) {
$content = $blockobject->decode_content_links($content,$restore);
}
return $content;
}
@@ -248,10 +260,10 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
}
//This function read the xml file and store its data from the blocks in a object
function restore_read_xml_blocks ($xml_file) {
function restore_read_xml_blocks ($restore, $xml_file) {
//We call the main read_xml function, with todo = BLOCKS
$info = restore_read_xml ($xml_file,'BLOCKS',false);
$info = restore_read_xml ($xml_file,'BLOCKS',$restore);
return $info;
}
@@ -762,7 +774,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
// Looks like it's from Moodle < 1.3. Let's give the course default blocks...
$newpage = page_create_object(PAGE_COURSE_VIEW, $restore->course_id);
blocks_repopulate_page($newpage);
} else {
} else if (!empty($CFG->showblocksonmodpages)) {
// We just have a blockinfo field, this is a legacy 1.4 or 1.3 backup
$blockrecords = get_records_select('block', '', '', 'name, id');
$temp_blocks_l = array();
@@ -810,17 +822,13 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
$status = true;
// Tracks which blocks we create during the restore.
// This is used in restore_decode_content_links.
$restore->blockinstanceids = array();
//Check it exists
if (!file_exists($xml_file)) {
$status = false;
}
//Get info from xml
if ($status) {
$info = restore_read_xml_blocks($xml_file);
$info = restore_read_xml_blocks($restore,$xml_file);
}
if(empty($info->instances)) {
@@ -838,8 +846,8 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
if($instance->pagetype == PAGE_COURSE_VIEW) {
// This one's easy...
$instance->pageid = $restore->course_id;
}
else {
} else if (!empty($CFG->showblocksonmodpages)) {
$parts = explode('-', $instance->pagetype);
if($parts[0] == 'mod') {
if(!$restore->mods[$parts[1]]->restore) {
@@ -857,6 +865,10 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
// Not invented here ;-)
continue;
}
} else {
// do not restore activity blocks if disabled
continue;
}
if(!isset($pageinstances[$instance->pagetype])) {
@@ -869,7 +881,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
$pageinstances[$instance->pagetype][$instance->pageid][] = $instance;
}
$blocks = get_records_select('block', '', '', 'name, id, multiple');
$blocks = get_records_select('block', 'visible = 1', '', 'name, id, multiple');
// For each type of page we have restored
foreach($pageinstances as $thistypeinstances) {
@@ -907,11 +919,35 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
//Add this instance
$instance->blockid = $blocks[$instance->name]->id;
if ($newid = insert_record('block_instance', $instance)) {
if (!empty($instance->id)) { // this will only be set if we come from 1.7 and above backups
backup_putid ($restore->backup_unique_code,"block_instance",$instance->id,$newid);
// This will only be set if we come from 1.7 and above backups
// Also, must do this before insert (insert_record unsets id)
if (!empty($instance->id)) {
$oldid = $instance->id;
} else {
$oldid = 0;
}
if ($instance->id = insert_record('block_instance', $instance)) {
// Create block instance
if (!$blockobj = block_instance($instance->name, $instance)) {
$status = false;
break;
}
$restore->blockinstanceids[] = $newid;
// Run the block restore if needed
if ($blockobj->backuprestore_instancedata_used()) {
// Get restore information
$data = backup_getid($restore->backup_unique_code,'block_instance',$oldid);
$data->new_id = $instance->id; // For completeness
if (!$blockobj->instance_restore($restore, $data)) {
$status = false;
break;
}
}
// Save oldid after block restore process because info will be over-written with blank string
if ($oldid) {
backup_putid ($restore->backup_unique_code,"block_instance",$oldid,$instance->id);
}
} else {
$status = false;
break;
@@ -919,8 +955,9 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
//Get an object for the block and tell it it's been restored so it can update dates
//etc. if necessary
$blockobj=block_instance($instance->name,$instance);
$blockobj->after_restore($restore);
if ($blockobj = block_instance($instance->name,$instance)) {
$blockobj->after_restore($restore);
}
//Now we can increment the weight counter
++$maxweights[$instance->position];
@@ -4286,6 +4323,16 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
//Check if we are into BLOCKS zone
//if ($this->tree[3] == "BLOCKS") //Debug
// echo $this->level.str_repeat("&nbsp;",$this->level*2)."&lt;".$tagName."&gt;<br />\n"; //Debug
//If we are under a BLOCK tag under a BLOCKS zone, accumule it
if (isset($this->tree[4]) and isset($this->tree[3])) { //
if ($this->tree[4] == "BLOCK" and $this->tree[3] == "BLOCKS") {
if (!isset($this->temp)) {
$this->temp = "";
}
$this->temp .= "<".$tagName.">";
}
}
}
//This is the startTag handler we use where we are reading the sections zone (todo="SECTIONS")
@@ -5077,6 +5124,13 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
//if (trim($this->content)) //Debug
// echo "C".str_repeat("&nbsp;",($this->level+2)*2).$this->getContents()."<br />\n"; //Debug
//echo $this->level.str_repeat("&nbsp;",$this->level*2)."&lt;/".$tagName."&gt;<br />\n"; //Debug
// Collect everything into $this->temp
if (!isset($this->temp)) {
$this->temp = "";
}
$this->temp .= htmlspecialchars(trim($this->content))."</".$tagName.">";
//Dependig of different combinations, do different things
if ($this->level == 4) {
switch ($tagName) {
@@ -5084,6 +5138,37 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
//We've finalized a block, get it
$this->info->instances[] = $this->info->tempinstance;
unset($this->info->tempinstance);
//Also, xmlize INSTANCEDATA and save to db
//Prepend XML standard header to info gathered
$xml_data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".$this->temp;
//Call to xmlize for this portion of xml data (one BLOCK)
//echo "-XMLIZE: ".strftime ("%X",time()),"-"; //Debug
$data = xmlize($xml_data,0);
//echo strftime ("%X",time())."<p>"; //Debug
//traverse_xmlize($data); //Debug
//print_object ($GLOBALS['traverse_array']); //Debug
//$GLOBALS['traverse_array']=""; //Debug
//Check for instancedata, is exists, then save to DB
if (isset($data['BLOCK']['#']['INSTANCEDATA']['0']['#'])) {
//Get old id
$oldid = $data['BLOCK']['#']['ID']['0']['#'];
//Get instancedata
if ($data = $data['BLOCK']['#']['INSTANCEDATA']['0']['#']) {
//Restore code calls this multiple times - so might already have the newid
if ($newid = backup_getid($this->preferences->backup_unique_code,'block_instance',$oldid)) {
$newid = $newid->new_id;
} else {
$newid = null;
}
//Save to DB, we will use it later
$status = backup_putid($this->preferences->backup_unique_code,'block_instance',$oldid,$newid,$data);
}
}
//Reset temp
unset($this->temp);
break;
default:
die($tagName);
@@ -8105,7 +8190,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string("creatingblocksroles").'</li>';
}
$blocks = restore_read_xml_blocks($xmlfile);
$blocks = restore_read_xml_blocks($restore, $xmlfile);
if (isset($blocks->instances)) {
foreach ($blocks->instances as $instance) {
if (isset($instance->roleassignments) && !$isimport) {
+43 -6
View File
@@ -36,14 +36,51 @@ class block_html extends block_base {
return $this->content;
}
function backup_encode_absolute_links_in_config(&$config) {
$config->text = backup_encode_absolute_links($config->text);
/**
* Will be called before an instance of this block is backed up, so that any links in
* any links in any HTML fields on config can be encoded.
* @return string
*/
function get_backup_encoded_config() {
$data = clone($this->config);
$data->text = backup_encode_absolute_links($data->text);
return base64_encode(serialize($data));
}
function restore_decode_absolute_links_in_config(&$config) {
$oldtext = $config->text;
$config->text = restore_decode_absolute_links($oldtext);
return $config->text != $oldtext;
/**
* This function makes all the necessary calls to {@link restore_decode_content_links_worker()}
* function in order to decode contents of this block from the backup
* format to destination site/course in order to mantain inter-activities
* working in the backup/restore process.
*
* This is called from {@link restore_decode_content_links()} function in the restore process.
*
* NOTE: There is no block instance when this method is called.
*
* @param object $restore Standard restore object
* @return boolean
**/
function decode_content_links_caller($restore) {
global $CFG;
if ($restored_blocks = get_records_select("backup_ids","table_name = 'block_instance' AND backup_code = $restore->backup_unique_code AND new_id > 0", "", "new_id")) {
$restored_blocks = implode(',', array_keys($restored_blocks));
$sql = "SELECT bi.*
FROM {$CFG->prefix}block_instance bi
JOIN {$CFG->prefix}block b ON b.id = bi.blockid
WHERE b.name = 'html' AND bi.id IN ($restored_blocks)";
if ($instances = get_records_sql($sql)) {
foreach ($instances as $instance) {
$blockobject = block_instance('html', $instance);
$blockobject->config->text = restore_decode_absolute_links($blockobject->config->text);
$blockobject->config->text = restore_decode_content_links_worker($blockobject->config->text, $restore);
$blockobject->instance_config_commit($blockobject->pinned);
}
}
}
return true;
}
}
?>
+90 -13
View File
@@ -130,25 +130,102 @@ class block_base {
}
/**
* Will be called before an instance of this block is backed up, so that any links in
* any links in any HTML fields on config can be encoded. For example, for the HTML block
* we need to do $config->text = backup_encode_absolute_links($config->text);.
* Enable custom instance data section in backup and restore.
*
* If return true, then {@link instance_backup()} and
* {@link instance_restore()} will be called during
* backup/restore routines.
*
* @param object $config the config field for an insance of this class.
*/
function backup_encode_absolute_links_in_config(&$config) {
* @return boolean
**/
function backuprestore_instancedata_used() {
return false;
}
/**
* Undo the effect of backup_encode_absolute_links_in_config. For exmaple, in the
* HTML block we need to do $config->text = restore_decode_absolute_links($config->text);
* Allows the block class to have a backup routine. Handy
* when the block has its own tables that have foreign keys to
* other tables (example: user table).
*
* Note: at the time of writing this comment, the indent level
* for the {@link full_tag()} should start at 5.
*
* @param object $config the config field for an insance of this class.
* @return boolean return true if something has changed, so the database can be updated,
* false if not, for efficiency reasons.
* @param resource $bf Backup File
* @param object $preferences Backup preferences
* @return boolean
**/
function instance_backup($bf, $preferences) {
return true;
}
/**
* Allows the block class to restore its backup routine.
*
* Should not return false if data is empty
* because old backups would not contain block instance backup data.
*
* @param object $restore Standard restore object
* @param object $data Object from backup_getid for this block instance
* @return boolean
**/
function instance_restore($restore, $data) {
return true;
}
/**
* Will be called before an instance of this block is backed up, so that any links in
* in config can be encoded. For example config->text, for the HTML block
* @return string
*/
function restore_decode_absolute_links_in_config(&$config) {
return false;
function get_backup_encoded_config() {
return base64_encode(serialize($this->config));
}
/**
* Return the content encoded to support interactivities linking. This function is
* called automatically from the backup procedure by {@link backup_encode_absolute_links()}.
*
* NOTE: There is no block instance when this method is called.
*
* @param string $content Content to be encoded
* @param object $restore Restore preferences object
* @return string The encoded content
**/
function encode_content_links($content, $restore) {
return $content;
}
/**
* This function makes all the necessary calls to {@link restore_decode_content_links_worker()}
* function in order to decode contents of this block from the backup
* format to destination site/course in order to mantain inter-activities
* working in the backup/restore process.
*
* This is called from {@link restore_decode_content_links()} function in the restore process.
*
* NOTE: There is no block instance when this method is called.
*
* @param object $restore Standard restore object
* @return boolean
**/
function decode_content_links_caller($restore) {
return true;
}
/**
* Return content decoded to support interactivities linking.
* This is called automatically from
* {@link restore_decode_content_links_worker()} function
* in the restore process.
*
* NOTE: There is no block instance when this method is called.
*
* @param string $content Content to be dencoded
* @param object $restore Restore preferences object
* @return string The dencoded content
**/
function decode_content_links($content, $restore) {
return $content;
}
/**