Merge branch 'MDL-33430-reference-restore' of git://github.com/mudrd8mz/moodle

Conflicts:
	lib/db/install.xml
	lib/db/upgrade.php
	version.php
This commit is contained in:
Sam Hemelryk
2012-06-22 11:56:49 +12:00
13 changed files with 522 additions and 55 deletions
+7 -6
View File
@@ -1404,7 +1404,8 @@ class backup_final_files_structure_step extends backup_structure_step {
'contenthash', 'contextid', 'component', 'filearea', 'itemid',
'filepath', 'filename', 'userid', 'filesize',
'mimetype', 'status', 'timecreated', 'timemodified',
'source', 'author', 'license', 'sortorder', 'reference', 'repositoryid'));
'source', 'author', 'license', 'sortorder',
'repositorytype', 'repositoryid', 'reference'));
// Build the tree
@@ -1412,12 +1413,12 @@ class backup_final_files_structure_step extends backup_structure_step {
// Define sources
$file->set_source_sql("SELECT f.*, r.repositoryid, r.reference
$file->set_source_sql("SELECT f.*, r.type AS repositorytype, fr.repositoryid, fr.reference
FROM {files} f
LEFT JOIN {files_reference} r
ON r.id = f.referencefileid
JOIN {backup_ids_temp} bi
ON f.id = bi.itemid
LEFT JOIN {files_reference} fr ON fr.id = f.referencefileid
LEFT JOIN {repository_instances} ri ON ri.id = fr.repositoryid
LEFT JOIN {repository} r ON r.id = ri.typeid
JOIN {backup_ids_temp} bi ON f.id = bi.itemid
WHERE bi.backupid = ?
AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
+7 -1
View File
@@ -83,9 +83,15 @@ class restore_final_task extends restore_task {
// executing it to perform some final adjustments of information
// not available when the task was executed.
// This step is always the last one performing modifications on restored information
// Don't add any new step after it. Only cache rebuild and clean are allowed.
// Don't add any new step after it. Only aliases queue, cache rebuild and clean are allowed.
$this->add_step(new restore_execute_after_restore('executing_after_restore'));
// All files were sent to the filepool by now. We need to process
// the aliases yet as they were not actually created but stashed for us instead.
// We execute this step after executing_after_restore so that there can't be no
// more files sent to the filepool after this.
$this->add_step(new restore_process_file_aliases_queue('process_file_aliases_queue'));
// Rebuild course cache to see results, whoah!
$this->add_step(new restore_rebuild_course_cache('rebuild_course_cache'));
+327 -11
View File
@@ -591,22 +591,14 @@ class restore_load_included_files extends restore_structure_step {
}
/**
* Processing functions go here
* Process one <file> element from files.xml
*
* @param array $data one file record including repositoryid and reference
* @param array $data the element data
*/
public function process_file($data) {
$data = (object)$data; // handy
$isreference = !empty($data->repositoryid);
$issamesite = $this->task->is_samesite();
// If it's not samesite, we skip file refernces
if (!$issamesite && $isreference) {
return;
}
// load it if needed:
// - it it is one of the annotated inforef files (course/section/activity/block)
// - it is one "user", "group", "grouping", "grade", "question" or "qtype_xxxx" component file (that aren't sent to inforef ever)
@@ -617,7 +609,6 @@ class restore_load_included_files extends restore_structure_step {
$data->component == 'grouping' || $data->component == 'grade' ||
$data->component == 'question' || substr($data->component, 0, 5) == 'qtype');
if ($isfileref || $iscomponent) {
// Process files
restore_dbops::set_backup_files_record($this->get_restoreid(), $data);
}
}
@@ -3102,6 +3093,331 @@ class restore_create_question_files extends restore_execution_step {
}
}
/**
* Try to restore aliases and references to external files.
*
* The queue of these files was prepared for us in {@link restore_dbops::send_files_to_pool()}.
* We expect that all regular (non-alias) files have already been restored. Make sure
* there is no restore step executed after this one that would call send_files_to_pool() again.
*
* You may notice we have hardcoded support for Server files, Legacy course files
* and user Private files here at the moment. This could be eventually replaced with a set of
* callbacks in the future if needed.
*
* @copyright 2012 David Mudrak <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_process_file_aliases_queue extends restore_execution_step {
/** @var array internal cache for {@link choose_repository() */
private $cachereposbyid = array();
/** @var array internal cache for {@link choose_repository() */
private $cachereposbytype = array();
/**
* What to do when this step is executed.
*/
protected function define_execution() {
global $DB;
$this->log('processing file aliases queue', backup::LOG_INFO);
$fs = get_file_storage();
// Load the queue.
$rs = $DB->get_recordset('backup_ids_temp',
array('backupid' => $this->get_restoreid(), 'itemname' => 'file_aliases_queue'),
'', 'info');
// Iterate over aliases in the queue.
foreach ($rs as $record) {
$info = unserialize(base64_decode($record->info));
// Try to pick a repository instance that should serve the alias.
$repository = $this->choose_repository($info);
if (is_null($repository)) {
$this->notify_failure($info, 'unable to find a matching repository instance');
continue;
}
if ($info->oldfile->repositorytype === 'local' or $info->oldfile->repositorytype === 'coursefiles') {
// Aliases to Server files and Legacy course files may refer to a file
// contained in the backup file or to some existing file (if we are on the
// same site).
try {
$reference = file_storage::unpack_reference($info->oldfile->reference);
} catch (Exception $e) {
$this->notify_failure($info, 'invalid reference field format');
continue;
}
// Let's see if the referred source file was also included in the backup.
$candidates = $DB->get_recordset('backup_files_temp', array(
'backupid' => $this->get_restoreid(),
'contextid' => $reference['contextid'],
'component' => $reference['component'],
'filearea' => $reference['filearea'],
'itemid' => $reference['itemid'],
), '', 'info, newcontextid, newitemid');
$source = null;
foreach ($candidates as $candidate) {
$candidateinfo = unserialize(base64_decode($candidate->info));
if ($candidateinfo->filename === $reference['filename']
and $candidateinfo->filepath === $reference['filepath']
and !is_null($candidate->newcontextid)
and !is_null($candidate->newitemid) ) {
$source = $candidateinfo;
$source->contextid = $candidate->newcontextid;
$source->itemid = $candidate->newitemid;
break;
}
}
$candidates->close();
if ($source) {
// We have an alias that refers to another file also included in
// the backup. Let us change the reference field so that it refers
// to the restored copy of the original file.
$reference = file_storage::pack_reference($source);
// Send the new alias to the filepool.
$fs->create_file_from_reference($info->newfile, $repository->id, $reference);
$this->notify_success($info);
continue;
} else {
// This is a reference to some moodle file that was not contained in the backup
// file. If we are restoring to the same site, keep the reference untouched
// and restore the alias as is if the referenced file exists.
if ($this->task->is_samesite()) {
if ($fs->file_exists($reference['contextid'], $reference['component'], $reference['filearea'],
$reference['itemid'], $reference['filepath'], $reference['filename'])) {
$reference = file_storage::pack_reference($reference);
$fs->create_file_from_reference($info->newfile, $repository->id, $reference);
$this->notify_success($info);
continue;
} else {
$this->notify_failure($info, 'referenced file not found');
continue;
}
// If we are at other site, we can't restore this alias.
} else {
$this->notify_failure($info, 'referenced file not included');
continue;
}
}
} else if ($info->oldfile->repositorytype === 'user') {
if ($this->task->is_samesite()) {
// For aliases to user Private files at the same site, we have a chance to check
// if the referenced file still exists.
try {
$reference = file_storage::unpack_reference($info->oldfile->reference);
} catch (Exception $e) {
$this->notify_failure($info, 'invalid reference field format');
continue;
}
if ($fs->file_exists($reference['contextid'], $reference['component'], $reference['filearea'],
$reference['itemid'], $reference['filepath'], $reference['filename'])) {
$reference = file_storage::pack_reference($reference);
$fs->create_file_from_reference($info->newfile, $repository->id, $reference);
$this->notify_success($info);
continue;
} else {
$this->notify_failure($info, 'referenced file not found');
continue;
}
// If we are at other site, we can't restore this alias.
} else {
$this->notify_failure($info, 'restoring at another site');
continue;
}
} else {
// This is a reference to some external file such as in boxnet or dropbox.
// If we are restoring to the same site, keep the reference untouched and
// restore the alias as is.
if ($this->task->is_samesite()) {
$fs->create_file_from_reference($info->newfile, $repository->id, $info->oldfile->reference);
$this->notify_success($info);
continue;
// If we are at other site, we can't restore this alias.
} else {
$this->notify_failure($info, 'restoring at another site');
continue;
}
}
}
$rs->close();
}
/**
* Choose the repository instance that should handle the alias.
*
* At the same site, we can rely on repository instance id and we just
* check it still exists. On other site, try to find matching Server files or
* Legacy course files repository instance. Return null if no matching
* repository instance can be found.
*
* @param stdClass $info
* @return repository|null
*/
private function choose_repository(stdClass $info) {
global $DB, $CFG;
require_once($CFG->dirroot.'/repository/lib.php');
if ($this->task->is_samesite()) {
// We can rely on repository instance id.
if (array_key_exists($info->oldfile->repositoryid, $this->cachereposbyid)) {
return $this->cachereposbyid[$info->oldfile->repositoryid];
}
$this->log('looking for repository instance by id', backup::LOG_DEBUG, $info->oldfile->repositoryid, 1);
try {
$this->cachereposbyid[$info->oldfile->repositoryid] = repository::get_repository_by_id($info->oldfile->repositoryid, SYSCONTEXTID);
return $this->cachereposbyid[$info->oldfile->repositoryid];
} catch (Exception $e) {
$this->cachereposbyid[$info->oldfile->repositoryid] = null;
return null;
}
} else {
// We can rely on repository type only.
if (empty($info->oldfile->repositorytype)) {
return null;
}
if (array_key_exists($info->oldfile->repositorytype, $this->cachereposbytype)) {
return $this->cachereposbytype[$info->oldfile->repositorytype];
}
$this->log('looking for repository instance by type', backup::LOG_DEBUG, $info->oldfile->repositorytype, 1);
// Both Server files and Legacy course files repositories have a single
// instance at the system context to use. Let us try to find it.
if ($info->oldfile->repositorytype === 'local' or $info->oldfile->repositorytype === 'coursefiles') {
$sql = "SELECT ri.id
FROM {repository} r
JOIN {repository_instances} ri ON ri.typeid = r.id
WHERE r.type = ? AND ri.contextid = ?";
$ris = $DB->get_records_sql($sql, array($info->oldfile->repositorytype, SYSCONTEXTID));
if (empty($ris)) {
return null;
}
$repoids = array_keys($ris);
$repoid = reset($repoids);
try {
$this->cachereposbytype[$info->oldfile->repositorytype] = repository::get_repository_by_id($repoid, SYSCONTEXTID);
return $this->cachereposbytype[$info->oldfile->repositorytype];
} catch (Exception $e) {
$this->cachereposbytype[$info->oldfile->repositorytype] = null;
return null;
}
}
$this->cachereposbytype[$info->oldfile->repositorytype] = null;
return null;
}
}
/**
* Let the user know that the given alias was successfully restored
*
* @param stdClass $info
*/
private function notify_success(stdClass $info) {
$filedesc = $this->describe_alias($info);
$this->log('successfully restored alias', backup::LOG_DEBUG, $filedesc, 1);
}
/**
* Let the user know that the given alias can't be restored
*
* @param stdClass $info
* @param string $reason detailed reason to be logged
*/
private function notify_failure(stdClass $info, $reason = '') {
$filedesc = $this->describe_alias($info);
if ($reason) {
$reason = ' ('.$reason.')';
}
$this->log('unable to restore alias'.$reason, backup::LOG_WARNING, $filedesc, 1);
$this->add_result_item('file_aliases_restore_failures', $filedesc);
}
/**
* Return a human readable description of the alias file
*
* @param stdClass $info
* @return string
*/
private function describe_alias(stdClass $info) {
$filedesc = $this->expected_alias_location($info->newfile);
if (!is_null($info->oldfile->source)) {
$filedesc .= ' ('.$info->oldfile->source.')';
}
return $filedesc;
}
/**
* Return the expected location of a file
*
* Please note this may and may not work as a part of URL to pluginfile.php
* (depends on how the given component/filearea deals with the itemid).
*
* @param stdClass $filerecord
* @return string
*/
private function expected_alias_location($filerecord) {
$filedesc = '/'.$filerecord->contextid.'/'.$filerecord->component.'/'.$filerecord->filearea;
if (!is_null($filerecord->itemid)) {
$filedesc .= '/'.$filerecord->itemid;
}
$filedesc .= $filerecord->filepath.$filerecord->filename;
return $filedesc;
}
/**
* Append a value to the given resultset
*
* @param string $name name of the result containing a list of values
* @param mixed $value value to add as another item in that result
*/
private function add_result_item($name, $value) {
$results = $this->task->get_results();
if (isset($results[$name])) {
if (!is_array($results[$name])) {
throw new coding_exception('Unable to append a result item into a non-array structure.');
}
$current = $results[$name];
$current[] = $value;
$this->task->add_result(array($name => $current));
} else {
$this->task->add_result(array($name => array($value)));
}
}
}
/**
* Abstract structure step, to be used by all the activities using core questions stuff
* (like the quiz module), to support qtype plugins, states and sessions
+75 -31
View File
@@ -808,6 +808,17 @@ abstract class restore_dbops {
* Given one component/filearea/context and
* optionally one source itemname to match itemids
* put the corresponding files in the pool
*
* @param string $basepath the full path to the root of unzipped backup file
* @param string $restoreid the restore job's identification
* @param string $component
* @param string $filearea
* @param int $oldcontextid
* @param int $dfltuserid default $file->user if the old one can't be mapped
* @param string|null $itemname
* @param int|null $olditemid
* @param int|null $forcenewcontextid explicit value for the new contextid (skip mapping)
* @param bool $skipparentitemidctxmatch
*/
public static function send_files_to_pool($basepath, $restoreid, $component, $filearea, $oldcontextid, $dfltuserid, $itemname = null, $olditemid = null, $forcenewcontextid = null, $skipparentitemidctxmatch = false) {
global $DB;
@@ -839,17 +850,17 @@ abstract class restore_dbops {
// itemname = null, we are going to match only by context, no need to use itemid (all them are 0)
if ($itemname == null) {
$sql = 'SELECT contextid, component, filearea, itemid, itemid AS newitemid, info
$sql = "SELECT id AS bftid, contextid, component, filearea, itemid, itemid AS newitemid, info
FROM {backup_files_temp}
WHERE backupid = ?
AND contextid = ?
AND component = ?
AND filearea = ?';
AND filearea = ?";
$params = array($restoreid, $oldcontextid, $component, $filearea);
// itemname not null, going to join with backup_ids to perform the old-new mapping of itemids
} else {
$sql = "SELECT f.contextid, f.component, f.filearea, f.itemid, i.newitemid, f.info
$sql = "SELECT f.id AS bftid, f.contextid, f.component, f.filearea, f.itemid, i.newitemid, f.info
FROM {backup_files_temp} f
JOIN {backup_ids_temp} i ON i.backupid = f.backupid
$parentitemctxmatchsql
@@ -872,47 +883,80 @@ abstract class restore_dbops {
foreach ($rs as $rec) {
$file = (object)unserialize(base64_decode($rec->info));
$isreference = !empty($file->repositoryid);
// ignore root dirs (they are created automatically)
if ($file->filepath == '/' && $file->filename == '.') {
continue;
}
// set the best possible user
$mappeduser = self::get_backup_ids_record($restoreid, 'user', $file->userid);
$file->userid = !empty($mappeduser) ? $mappeduser->newitemid : $dfltuserid;
// dir found (and not root one), let's create if
$mappeduserid = !empty($mappeduser) ? $mappeduser->newitemid : $dfltuserid;
// dir found (and not root one), let's create it
if ($file->filename == '.') {
$fs->create_directory($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->userid);
$fs->create_directory($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $mappeduserid);
continue;
}
// arrived here, file found
// Find file in backup pool
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
if (empty($file->repositoryid)) {
// this is a regular file, it must be present in the backup pool
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
if (!file_exists($backuppath) && !$isreference) {
throw new restore_dbops_exception('file_not_found_in_pool', $file);
}
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
$file_record = array(
'contextid' => $newcontextid,
'component' => $component,
'filearea' => $filearea,
'itemid' => $rec->newitemid,
'filepath' => $file->filepath,
'filename' => $file->filename,
'timecreated' => $file->timecreated,
'timemodified'=> $file->timemodified,
'userid' => $file->userid,
'author' => $file->author,
'license' => $file->license,
'sortorder' => $file->sortorder);
if ($isreference) {
$fs->create_file_from_reference($file_record, $file->repositoryid, $file->reference);
} else {
if (!file_exists($backuppath)) {
throw new restore_dbops_exception('file_not_found_in_pool', $file);
}
// create the file in the filepool if it does not exist yet
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
$file_record = array(
'contextid' => $newcontextid,
'component' => $component,
'filearea' => $filearea,
'itemid' => $rec->newitemid,
'filepath' => $file->filepath,
'filename' => $file->filename,
'timecreated' => $file->timecreated,
'timemodified'=> $file->timemodified,
'userid' => $mappeduserid,
'author' => $file->author,
'license' => $file->license,
'sortorder' => $file->sortorder
);
$fs->create_file_from_pathname($file_record, $backuppath);
}
// store the the new contextid and the new itemid in case we need to remap
// references to this file later
$DB->update_record('backup_files_temp', array(
'id' => $rec->bftid,
'newcontextid' => $newcontextid,
'newitemid' => $rec->newitemid), true);
} else {
// this is an alias - we can't create it yet so we stash it in a temp
// table and will let the final task to deal with it
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
$info = new stdClass();
// oldfile holds the raw information stored in MBZ (including reference-related info)
$info->oldfile = $file;
// newfile holds the info for the new file_record with the context, user and itemid mapped
$info->newfile = (object)array(
'contextid' => $newcontextid,
'component' => $component,
'filearea' => $filearea,
'itemid' => $rec->newitemid,
'filepath' => $file->filepath,
'filename' => $file->filename,
'timecreated' => $file->timecreated,
'timemodified'=> $file->timemodified,
'userid' => $mappeduserid,
'author' => $file->author,
'license' => $file->license,
'sortorder' => $file->sortorder
);
restore_dbops::set_backup_ids_record($restoreid, 'file_aliases_queue', $file->id, 0, null, $info);
}
}
}
$rs->close();
+17
View File
@@ -75,10 +75,27 @@ abstract class base_plan implements checksumable, executable {
return $this->tasks;
}
/**
* Add the passed info to the plan results
*
* At the moment we expect an associative array structure to be merged into
* the current results. In the future, some sort of base_result class may
* be introduced.
*
* @param array $result associative array describing a result of a task/step
*/
public function add_result($result) {
if (!is_array($result)) {
throw new coding_exception('Associative array is expected as a parameter of add_result()');
}
$this->results = array_merge($this->results, $result);
}
/**
* Return the results collected via {@link self::add_result()} method
*
* @return array
*/
public function get_results() {
return $this->results;
}
+29 -1
View File
@@ -154,7 +154,7 @@ abstract class base_task implements checksumable, executable, loggable {
// If step returns array, it will be forwarded to plan
// (TODO: shouldn't be array but proper result object)
if (is_array($result) and !empty($result)) {
$this->plan->add_result($result);
$this->add_result($result);
}
}
// Mark as executed if any step has been executed
@@ -191,6 +191,34 @@ abstract class base_task implements checksumable, executable, loggable {
backup_general_helper::array_checksum_recursive($this->steps));
}
/**
* Add the given info to the current plan's results.
*
* @see base_plan::add_result()
* @param array $result associative array describing a result of a task/step
*/
public function add_result($result) {
if (!is_null($this->plan)) {
$this->plan->add_result($result);
} else {
debugging('Attempting to add a result of a task not binded with a plan', DEBUG_DEVELOPER);
}
}
/**
* Return the current plan's results
*
* @return array|null
*/
public function get_results() {
if (!is_null($this->plan)) {
return $this->plan->get_results();
} else {
debugging('Attempting to get results of a task not binded with a plan', DEBUG_DEVELOPER);
return null;
}
}
// Protected API starts here
/**
+14
View File
@@ -757,6 +757,20 @@ class restore_ui_stage_complete extends restore_ui_stage_process {
public function display(core_backup_renderer $renderer) {
$html = '';
if (!empty($this->results['file_aliases_restore_failures'])) {
$html .= $renderer->box_start('generalbox filealiasesfailures');
$html .= $renderer->heading_with_help(get_string('filealiasesrestorefailures', 'core_backup'),
'filealiasesrestorefailures', 'core_backup');
$html .= $renderer->container(get_string('filealiasesrestorefailuresinfo', 'core_backup'));
$html .= $renderer->container_start('aliaseslist');
$html .= html_writer::start_tag('ul');
foreach ($this->results['file_aliases_restore_failures'] as $alias) {
$html .= html_writer::tag('li', s($alias));
}
$html .= html_writer::end_tag('ul');
$html .= $renderer->container_end();
$html .= $renderer->box_end();
}
$html .= $renderer->box_start();
$html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
$html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
+6
View File
@@ -117,6 +117,12 @@ $string['errorinvalidformat'] = 'Unknown backup format';
$string['errorinvalidformatinfo'] = 'The selected file is not a valid Moodle backup file and can\'t be restored.';
$string['executionsuccess'] = 'The backup file was successfully created.';
$string['filename'] = 'Filename';
$string['filealiasesrestorefailures'] = 'Aliases restore failures';
$string['filealiasesrestorefailuresinfo'] = 'Some aliases included in the backup file could not be restored. The following list contains their expected location and the source file they were referring to at the original site.';
$string['filealiasesrestorefailures_help'] = 'Aliases are symbolic links to other files, including those stored in external repositories. In some cases, Moodle cannot restore them - for example when restoring the backup at another site or when the referenced file does not exist.
More details and the actual reason of the failure can be found in the restore log file.';
$string['filealiasesrestorefailures_link'] = 'restore/filealiases';
$string['filereferencesincluded'] = 'File references to external contents included in backup package, they won\'t work on other sites.';
$string['filereferencessamesite'] = 'Backup is from the same site, file references can be restored';
$string['filereferencesnotsamesite'] = 'Backup is from other site, file references cannot be restored';
+4 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20120618" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20120620" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -2752,7 +2752,9 @@
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="filearea"/>
<FIELD NAME="filearea" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="component" NEXT="itemid"/>
<FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="filearea" NEXT="info"/>
<FIELD NAME="info" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="to store the complete file record (serialized)" PREVIOUS="itemid"/>
<FIELD NAME="info" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="to store the complete file record (serialized)" PREVIOUS="itemid" NEXT="newcontextid"/>
<FIELD NAME="newcontextid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The new contextid that the file has been restored to." PREVIOUS="info" NEXT="newitemid"/>
<FIELD NAME="newitemid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The new itemid the file has been restored to." PREVIOUS="newcontextid"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+24
View File
@@ -847,6 +847,30 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012061800.01);
}
if ($oldversion < 2012062000.00) {
// Add field newcontextid to backup_files_template
$table = new xmldb_table('backup_files_template');
$field = new xmldb_field('newcontextid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'info');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_main_savepoint(true, 2012062000.00);
}
if ($oldversion < 2012062000.01) {
// Add field newitemid to backup_files_template
$table = new xmldb_table('backup_files_template');
$field = new xmldb_field('newitemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'newcontextid');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_main_savepoint(true, 2012062000.01);
}
return true;
}
+8 -1
View File
@@ -1659,7 +1659,14 @@ class file_storage {
* @return array
*/
public static function unpack_reference($str, $cleanparams = false) {
$params = unserialize(base64_decode($str));
$decoded = base64_decode($str, true);
if ($decoded === false) {
throw new file_reference_exception(null, $str, null, null, 'Invalid base64 format');
}
$params = @unserialize($decoded); // hide E_NOTICE
if ($params === false) {
throw new file_reference_exception(null, $decoded, null, null, 'Not an unserializeable value');
}
if (is_array($params) && $cleanparams) {
$params = array(
'component' => is_null($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT),
+3 -1
View File
@@ -461,6 +461,8 @@ body.tag .managelink {padding: 5px;}
.path-backup .backup_progress .backup_stage.backup_stage_current {font-weight:bold;color:inherit;}
.path-backup .backup_progress .backup_stage.backup_stage_next {}
.path-backup .backup_progress span.backup_stage.backup_stage_complete {color:inherit;}
#page-backup-restore .filealiasesfailures {background-color:#ffd3d9}
#page-backup-restore .filealiasesfailures .aliaseslist {width:90%;margin:0.8em auto;background-color:white;border:1px dotted #666;}
/**
* Web Service
@@ -973,4 +975,4 @@ sup {vertical-align: super;}
box-shadow: 0px 0px 10px 0px #CCCCCC;
-webkit-box-shadow: 0px 0px 10px 0px #CCCCCC;
-moz-box-shadow: 0px 0px 10px 0px #CCCCCC;
}
}
+1 -1
View File
@@ -30,7 +30,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2012061800.01; // YYYYMMDD = weekly release date of this DEV branch
$version = 2012062000.01; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes