MDL-23478 backup - restore blocks (instances an positions)

This commit is contained in:
Eloy Lafuente
2010-07-24 22:56:30 +00:00
parent 6eb2138a81
commit b8e455a789
5 changed files with 246 additions and 10 deletions
+77 -2
View File
@@ -31,12 +31,22 @@
abstract class restore_block_task extends restore_task {
protected $taskbasepath; // To store the basepath of this block
protected $blockname; // Name of the block
protected $contextid; // new (target) context of the block
protected $oldcontextid;// old (original) context of the block
protected $blockid; // new (target) id of the block
protected $oldblockid; // old (original) id of the block
/**
* Constructor - instantiates one object of this class
*/
public function __construct($name, $taskbasepath, $plan = null) {
$this->taskbasepath = $taskbasepath;
$this->blockname = '';
$this->contextid = 0;
$this->oldcontextid = 0;
$this->blockid = 0;
$this->oldblockid = 0;
parent::__construct($name, $plan);
}
@@ -58,15 +68,75 @@ abstract class restore_block_task extends restore_task {
return;
}
// TODO: See backup. If "child" of activity task and it has been excluded, nothing to do
// If "child" of activity task and it has been excluded, nothing to do
$parent = basename(dirname(dirname($this->taskbasepath)));
if ($parent != 'course') {
$includedsetting = $parent . '_included';
if (!$this->get_setting_value($includedsetting)) {
$this->built = true;
return;
}
}
// Process the block.xml common file (instance + positions)
$this->add_step(new restore_block_instance_structure_step('block_commons', 'block.xml'));
// TODO: Link all the activity steps here
// Here we add all the common steps for any block and, in the point of interest
// we call to define_my_steps() in order to get the particular ones inserted in place.
$this->define_my_steps();
// Restore block role assignments and overrides (internally will observe the role_assignments setting)
$this->add_step(new restore_ras_and_caps_structure_step('block_ras_and_caps', 'roles.xml'));
// Restore block comments (conditionally)
if ($this->get_setting_value('comments')) {
$this->add_step(new restore_comments_structure_step('block_comments', 'comments.xml'));
}
// At the end, mark it as built
$this->built = true;
}
public function set_blockname($blockname) {
$this->blockname = $blockname;
}
public function get_blockname() {
return $this->blockname;
}
public function set_blockid($blockid) {
$this->blockid = $blockid;
}
public function get_blockid() {
return $this->blockid;
}
public function set_old_blockid($blockid) {
$this->oldblockid = $blockid;
}
public function get_old_blockid() {
return $this->oldblockid;
}
public function set_contextid($contextid) {
$this->contextid = $contextid;
}
public function get_contextid() {
return $this->contextid;
}
public function set_old_contextid($contextid) {
$this->oldcontextid = $contextid;
}
public function get_old_contextid() {
return $this->oldcontextid;
}
// Protected API starts here
/**
@@ -90,6 +160,11 @@ abstract class restore_block_task extends restore_task {
*/
abstract protected function define_my_steps();
/**
* Define one array() of fileareas that each block controls
*/
abstract public function get_fileareas();
/**
* Define one array() of configdata attributes
* that need to be decoded
@@ -37,6 +37,10 @@ class restore_default_block_task extends restore_block_task {
protected function define_my_steps() {
}
public function get_fileareas() {
return array();
}
public function get_configdata_encoded_attributes() {
return array();
}
+7 -1
View File
@@ -35,9 +35,15 @@ class restore_final_task extends restore_task {
*/
public function build() {
// Review all the block_position records in backup_ids in order
// match them now that all the contexts are created populating DB
// as needed. Only if we are restoring blocks.
if ($this->get_setting_value('blocks')) {
$this->add_step(new restore_review_pending_block_positions('review_block_positions'));
}
// TODO: Gradebook
// TODO: interlinks
// TODO: Apply all the block_positions accumulated along the process
// Clean the temp dir (conditionally) and drop temp table
$this->add_step(new restore_drop_and_clean_temp_stuff('drop_and_clean_temp_stuff'));
@@ -117,7 +117,7 @@ abstract class restore_plan_builder {
$blocks = backup_general_helper::get_blocks_from_path($task->get_taskbasepath());
foreach ($blocks as $basepath => $name) {
if ($task = restore_factory::get_restore_block_task($name, $basepath)) {
$plan->add_task($task);
//$plan->add_task($task);
} else {
// TODO: Debug information about block not supported
}
+157 -6
View File
@@ -66,6 +66,37 @@ class restore_drop_and_clean_temp_stuff extends restore_execution_step {
}
}
/**
* Review all the (pending) block positions in backup_ids, matching by
* contextid, creating positions as needed. This is executed by the
* final task, once all the contexts have been created
*/
class restore_review_pending_block_positions extends restore_execution_step {
protected function define_execution() {
global $DB;
// Get all the block_position objects pending to match
$params = array('backupid' => $this->get_restoreid(), 'itemname' => 'block_position');
$rs = $DB->get_recordset('backup_ids_temp', $params, '', 'itemid');
// Process block positions, creating them or accumulating for final step
foreach($rs as $posrec) {
// Get the complete position object (stored as info)
$position = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'block_position', $posrec->itemid)->info;
// If position is for one already mapped (known) contextid
// process it now, creating the position, else nothing to
// do, position finally discarded
if ($newctx = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'context', $position->contextid)) {
$position->contextid = $newctx->newitemid;
// Create the block position
$DB->insert_record('block_positions', $position);
}
}
$rs->close();
}
}
/*
* Execution step that, *conditionally* (if there isn't preloaded information)
* will load the inforef files for all the included course/section/activity tasks
@@ -646,8 +677,8 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
// Check roleid, userid are one of the mapped ones
$newroleid = $this->get_mappingid('role', $data->roleid);
$newuserid = $this->get_mappingid('user', $data->userid);
// If newroleid and newuserid and component is empty assign via API (handles dupes and friends)
if ($newroleid && $newuserid && empty($data->component)) {
// If newroleid and newuserid and component is empty and context valid assign via API (handles dupes and friends)
if ($newroleid && $newuserid && empty($data->component) && $this->task->get_contextid()) {
// TODO: role_assign() needs one userid param to be able to specify our restore userid
role_assign($newroleid, $newuserid, $this->task->get_contextid());
}
@@ -658,9 +689,10 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
// Check roleid is one of the mapped ones
$newroleid = $this->get_mappingid('role', $data->roleid);
// If newroleid is valid assign it via API (it handles dupes and so on)
if ($newroleid) {
// If newroleid and context are valid assign it via API (it handles dupes and so on)
if ($newroleid && $this->task->get_contextid()) {
// TODO: assign_capability() needs one userid param to be able to specify our restore userid
// TODO: it seems that assign_capability() doesn't check for valid capabilities at all ???
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
}
}
@@ -820,9 +852,128 @@ class restore_comments_structure_step extends restore_structure_step {
}
// Only restore the comment if has no mapping OR we have found the matching mapping
if (!$mapping || $newitemid) {
if ($data->userid = $this->get_mappingid('user', $data->userid)) {
// Only if user mapping and context
$data->userid = $this->get_mappingid('user', $data->userid);
if ($data->userid && $this->task->get_contextid()) {
$data->contextid = $this->task->get_contextid();
$DB->insert_record('comments', $data);
// Only if there is another comment with same context/user/timecreated
$params = array('contextid' => $data->contextid, 'userid' => $data->userid, 'timecreated' => $data->timecreated);
if (!$DB->record_exists('comments', $params)) {
$DB->insert_record('comments', $data);
}
}
}
}
}
/**
* This structure steps restores one instance + positions of one block
* Note: Positions corresponding to one existing context are restored
* here, but all the ones having unknown contexts are sent to backup_ids
* for a later chance to be restored at the end (final task)
*/
class restore_block_instance_structure_step extends restore_structure_step {
protected function define_structure() {
$paths = array();
$paths[] = new restore_path_element('block', '/block', true); // Get the whole XML together
$paths[] = new restore_path_element('block_position', '/block/block_positions/block_position');
return $paths;
}
public function process_block($data) {
global $DB;
$data = (object)$data; // Handy
$oldcontextid = $data->contextid;
$oldid = $data->id;
$positions = isset($data->block_positions['block_position']) ? $data->block_positions['block_position'] : array();
// Look for the parent contextid
if (!$data->parentcontextid = $this->get_mappingid('context', $data->parentcontextid)) {
throw new restore_step_exception('restore_block_missing_parent_ctx', $data->parentcontextid);
}
// If there is already one block of that type in the parent context
// and the block is not multiple, stop processing
if ($DB->record_exists_sql("SELECT bi.id
FROM {block_instances} bi
JOIN {block} b ON b.name = bi.blockname
WHERE bi.parentcontextid = ?
AND bi.blockname = ?
AND b.multiple = 0", array($data->parentcontextid, $data->blockname))) {
return false;
}
// If there is already one block of that type in the parent context
// with the same showincontexts, pagetypepattern, subpagepattern, defaultregion and configdata
// stop processing
$params = array(
'blockname' => $data->blockname, 'parentcontextid' => $data->parentcontextid,
'showinsubcontexts' => $data->showinsubcontexts, 'pagetypepattern' => $data->pagetypepattern,
'subpagepattern' => $data->subpagepattern, 'defaultregion' => $data->defaultregion);
if ($birecs = $DB->get_records('block_instances', $params)) {
foreach($birecs as $birec) {
if ($birec->configdata == $data->configdata) {
return false;
}
}
}
// Set task old contextid, blockid and blockname once we know them
$this->task->set_old_contextid($oldcontextid);
$this->task->set_old_blockid($oldid);
$this->task->set_blockname($data->blockname);
// Let's look for anything within configdata neededing processing
// (nulls and uses of legacy file.php)
if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
$configdata = (array)unserialize(base64_decode($data->configdata));
foreach ($configdata as $attribute => $value) {
if (in_array($attribute, $attrstotransform)) {
$configdata[$attribute] = $this->contentprocessor->process_cdata($value);
}
}
$data->configdata = base64_encode(serialize((object)$configdata));
}
// Create the block instance
$newitemid = $DB->insert_record('block_instances', $data);
// Save the mapping (with restorefiles support)
$this->set_mapping('block_instance', $oldid, $newitemid, true);
// Create the block context
$newcontextid = get_context_instance(CONTEXT_BLOCK, $newitemid)->id;
// Save the block contexts mapping and sent it to task
$this->set_mapping('context', $oldcontextid, $newcontextid);
$this->task->set_contextid($newcontextid);
$this->task->set_blockid($newitemid);
// Restore block fileareas if declared
$component = 'block_' . $this->task->get_blockname();
foreach ($this->task->get_fileareas() as $filearea) { // Simple match by contextid. No itemname needed
$this->add_related_files($component, $filearea, null);
}
// Process block positions, creating them or accumulating for final step
foreach($positions as $position) {
$position = (object)$position;
$position->blockinstanceid = $newitemid; // The instance is always the restored one
// If position is for one already mapped (known) contextid
// process it now, creating the position
if ($newpositionctxid = $this->get_mappingid('context', $position->contextid)) {
$position->contextid = $newpositionctxid;
// Create the block position
$DB->insert_record('block_positions', $position);
// The position belongs to an unknown context, send it to backup_ids
// to process them as part of the final steps of restore. We send the
// whole $position object there, hence use the low level method.
} else {
restore_dbops::set_backup_ids_record($this->get_restoreid(), 'block_position', $position->id, 0, null, $position);
}
}
}