mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
MDL-27376 MDL-27377 Backup converters API refactored
* Several base_converter methods made protected when there was no obvious reason why they should be public (subject of eventual change still). * The conversion chain now constructed in advance before any converter class is instantiated, using Dijkstra's algorithm.
This commit is contained in:
parent
e48477d94f
commit
0164592b8c
@ -40,6 +40,8 @@ abstract class backup implements checksumable {
|
|||||||
|
|
||||||
// Backup format
|
// Backup format
|
||||||
const FORMAT_MOODLE = 'moodle2';
|
const FORMAT_MOODLE = 'moodle2';
|
||||||
|
const FORMAT_MOODLE1 = 'moodle1';
|
||||||
|
const FORMAT_IMSCC = 'imscc';
|
||||||
const FORMAT_UNKNOWN = 'unknown';
|
const FORMAT_UNKNOWN = 'unknown';
|
||||||
|
|
||||||
// Interactive
|
// Interactive
|
||||||
|
@ -377,17 +377,17 @@ class restore_controller extends backup implements loggable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert from current format to backup::MOODLE format
|
* Converts from current format to backup::MOODLE format
|
||||||
*/
|
*/
|
||||||
public function convert() {
|
public function convert() {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||||
|
|
||||||
if ($this->status != backup::STATUS_REQUIRE_CONV) {
|
if ($this->status != backup::STATUS_REQUIRE_CONV) {
|
||||||
throw new restore_controller_exception('cannot_convert_not_required_status');
|
throw new restore_controller_exception('cannot_convert_not_required_status');
|
||||||
}
|
}
|
||||||
require_once($CFG->dirroot.'/backup/util/includes/convert_includes.php');
|
|
||||||
|
|
||||||
// Run conversion until we have the proper format
|
// Run conversion to the proper format
|
||||||
convert_helper::to_moodle2_format($this->get_tempdir(), $this->format);
|
convert_helper::to_moodle2_format($this->get_tempdir(), $this->format);
|
||||||
|
|
||||||
// If no exceptions were thrown, then we are in the proper format
|
// If no exceptions were thrown, then we are in the proper format
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All of the task and step classes specific to moodle1 conversion
|
|
||||||
*
|
|
||||||
* @package core
|
* @package core
|
||||||
* @subpackage backup-convert
|
* @subpackage backup-convert
|
||||||
* @copyright 2011 Mark Nielsen <mark@moodlerooms.com>
|
* @copyright 2011 Mark Nielsen <mark@moodlerooms.com>
|
||||||
@ -30,45 +28,43 @@ require_once($CFG->dirroot.'/backup/converter/moodle1/taskslib.php');
|
|||||||
require_once($CFG->dirroot . '/backup/converter/moodle1/stepslib.php');
|
require_once($CFG->dirroot . '/backup/converter/moodle1/stepslib.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will be the Moodle 1 to Moodle 2 Converter
|
* Converter of Moodle 1.9 backup into Moodle 2.x format
|
||||||
*/
|
*/
|
||||||
class moodle1_converter extends plan_converter {
|
class moodle1_converter extends plan_converter {
|
||||||
/**
|
|
||||||
* The current module being processed
|
/** @var string the current module being processed */
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $currentmod = '';
|
protected $currentmod = '';
|
||||||
|
|
||||||
/**
|
/** @var string the current block being processed */
|
||||||
* The current block being processed
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $currentblock = '';
|
protected $currentblock = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean
|
* Detects the Moodle 1.9 format of the backup directory
|
||||||
|
*
|
||||||
|
* @param string $tempdir the name of the backup directory
|
||||||
|
* @return null|string backup::FORMAT_MOODLE1 if the Moodle 1.9 is detected, null otherwise
|
||||||
*/
|
*/
|
||||||
public function can_convert() {
|
public static function detect_format($tempdir) {
|
||||||
// Then look for MOODLE1 (moodle1) format
|
global $CFG;
|
||||||
$filepath = $this->get_tempdir() . '/moodle.xml';
|
|
||||||
if (file_exists($filepath)) { // Looks promising, lets load some information
|
$filepath = $CFG->dataroot . '/temp/backup/' . $tempdir . '/moodle.xml';
|
||||||
$handle = fopen($filepath, "r");
|
if (file_exists($filepath)) {
|
||||||
|
// looks promising, lets load some information
|
||||||
|
$handle = fopen($filepath, 'r');
|
||||||
$first_chars = fread($handle, 200);
|
$first_chars = fread($handle, 200);
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
|
|
||||||
// Check if it has the required strings
|
// check if it has the required strings
|
||||||
if (strpos($first_chars,'<?xml version="1.0" encoding="UTF-8"?>') !== false &&
|
if (strpos($first_chars,'<?xml version="1.0" encoding="UTF-8"?>') !== false and
|
||||||
strpos($first_chars,'<MOODLE_BACKUP>') !== false &&
|
strpos($first_chars,'<MOODLE_BACKUP>') !== false and
|
||||||
strpos($first_chars,'<INFO>') !== false) {
|
strpos($first_chars,'<INFO>') !== false) {
|
||||||
|
|
||||||
return true;
|
return backup::FORMAT_MOODLE1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path transformation for modules and blocks. Here we
|
* Path transformation for modules and blocks. Here we
|
||||||
@ -116,7 +112,7 @@ class moodle1_converter extends plan_converter {
|
|||||||
|
|
||||||
public function build_plan() {
|
public function build_plan() {
|
||||||
$this->xmlparser = new progressive_parser();
|
$this->xmlparser = new progressive_parser();
|
||||||
$this->xmlparser->set_file($this->get_tempdir() . '/moodle.xml');
|
$this->xmlparser->set_file($this->get_tempdir_path() . '/moodle.xml');
|
||||||
$this->xmlprocessor = new convert_structure_parser_processor($this); // @todo Probably move this
|
$this->xmlprocessor = new convert_structure_parser_processor($this); // @todo Probably move this
|
||||||
$this->xmlparser->set_processor($this->xmlprocessor);
|
$this->xmlparser->set_processor($this->xmlprocessor);
|
||||||
|
|
||||||
|
@ -26,15 +26,13 @@
|
|||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->dirroot.'/backup/util/includes/convert_includes.php');
|
require_once($CFG->dirroot . '/backup/converter/moodle1/converter.class.php');
|
||||||
|
|
||||||
class moodle1_converter_test extends UnitTestCase {
|
class moodle1_converter_test extends UnitTestCase {
|
||||||
|
|
||||||
public static $includecoverage = array();
|
public static $includecoverage = array();
|
||||||
|
|
||||||
/**
|
/** @var string the name of the directory containing the unpacked Moodle 1.9 backup */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $tempdir;
|
protected $tempdir;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
@ -43,7 +41,7 @@ class moodle1_converter_test extends UnitTestCase {
|
|||||||
$this->tempdir = convert_helper::generate_id('simpletest');
|
$this->tempdir = convert_helper::generate_id('simpletest');
|
||||||
check_dir_exists("$CFG->dataroot/temp/backup/$this->tempdir");
|
check_dir_exists("$CFG->dataroot/temp/backup/$this->tempdir");
|
||||||
copy(
|
copy(
|
||||||
$CFG->dirroot.'/backup/converter/moodle1/simpletest/files/moodle.xml',
|
"$CFG->dirroot/backup/converter/moodle1/simpletest/files/moodle.xml",
|
||||||
"$CFG->dataroot/temp/backup/$this->tempdir/moodle.xml"
|
"$CFG->dataroot/temp/backup/$this->tempdir/moodle.xml"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -55,10 +53,9 @@ class moodle1_converter_test extends UnitTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_can_convert() {
|
public function test_detect_format() {
|
||||||
$converter = convert_factory::converter('moodle1', $this->tempdir);
|
$detected = moodle1_converter::detect_format($this->tempdir);
|
||||||
$this->assertIsA($converter, 'moodle1_converter');
|
$this->assertEqual(backup::FORMAT_MOODLE1, $detected);
|
||||||
$this->assertTrue($converter->can_convert());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_convert() {
|
public function test_convert() {
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||||
|
|
||||||
class moodle1_root_task extends convert_task {
|
class moodle1_root_task extends convert_task {
|
||||||
/**
|
/**
|
||||||
* Function responsible for building the steps of any task
|
* Function responsible for building the steps of any task
|
||||||
|
@ -25,95 +25,195 @@
|
|||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Abstract Converter
|
* Base abstract converter
|
||||||
*
|
*
|
||||||
* @throws backup_exception|Exception|null
|
* @throws backup_exception|Exception|null
|
||||||
*/
|
*/
|
||||||
abstract class base_converter {
|
abstract class base_converter {
|
||||||
|
|
||||||
|
/** @var string unique identifier of this converter instance */
|
||||||
protected $id;
|
protected $id;
|
||||||
|
/** @var string the name of the directory containing the unpacked backup being converted */
|
||||||
protected $tempdir;
|
protected $tempdir;
|
||||||
protected $convertdir;
|
/** @var string the name of the directory where the backup is converted to */
|
||||||
|
protected $workdir;
|
||||||
|
|
||||||
// do we want absolute path instead of tempdir?
|
/**
|
||||||
// Do we need to create a new tempdir to convert into? EG: target...
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param string $tempdir the relative path to the directory containing the unpacked backup to convert
|
||||||
|
*/
|
||||||
public function __construct($tempdir) {
|
public function __construct($tempdir) {
|
||||||
|
|
||||||
$this->tempdir = $tempdir;
|
$this->tempdir = $tempdir;
|
||||||
$this->convertdir = $this->tempdir.'_'.$this->get_name();
|
$this->id = convert_helper::generate_id($this->workdir);
|
||||||
$this->id = convert_helper::generate_id($this->convertdir);
|
$this->workdir = $tempdir . '_' . $this->get_name() . '_' . $this->id;
|
||||||
$this->init();
|
$this->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init() {
|
/**
|
||||||
}
|
* Get instance identifier
|
||||||
|
*
|
||||||
|
* @return string the unique identifier of this converter instance
|
||||||
|
*/
|
||||||
public function get_id() {
|
public function get_id() {
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get converter name
|
||||||
|
*
|
||||||
|
* @return string the system name of the converter
|
||||||
|
*/
|
||||||
public function get_name() {
|
public function get_name() {
|
||||||
return array_shift(explode('_', get_class($this)));
|
return array_shift(explode('_', get_class($this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_convertdir() {
|
|
||||||
global $CFG;
|
|
||||||
|
|
||||||
return "$CFG->dataroot/temp/backup/$this->convertdir";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_tempdir() {
|
|
||||||
global $CFG;
|
|
||||||
|
|
||||||
return "$CFG->dataroot/temp/backup/$this->tempdir";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete_convertdir() {
|
|
||||||
fulldelete($this->get_convertdir());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create_convertdir() {
|
|
||||||
$this->delete_convertdir();
|
|
||||||
if (!check_dir_exists($this->get_convertdir())) {
|
|
||||||
throw new backup_exception('failedtomakeconvertdir'); // @todo Define this string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function replace_tempdir() {
|
|
||||||
fulldelete($this->get_tempdir());
|
|
||||||
|
|
||||||
if (!rename($this->get_convertdir(), $this->get_tempdir())) {
|
|
||||||
throw new backup_exception('failedmoveconvertedintoplace'); // @todo Define this string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* Converts the backup directory
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
abstract public function can_convert();
|
|
||||||
|
|
||||||
// Kicks things off
|
|
||||||
public function convert() {
|
public function convert() {
|
||||||
|
|
||||||
$e = NULL;
|
$e = null;
|
||||||
|
|
||||||
|
// try to execute the converter
|
||||||
try {
|
try {
|
||||||
$this->create_convertdir();
|
$this->create_workdir();
|
||||||
$this->execute();
|
$this->execute();
|
||||||
$this->replace_tempdir();
|
$this->replace_tempdir();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
}
|
}
|
||||||
// Do cleanup...
|
|
||||||
|
// clean-up stuff if needed
|
||||||
$this->destroy();
|
$this->destroy();
|
||||||
|
|
||||||
|
// eventually re-throw the execution exception
|
||||||
if ($e instanceof Exception) {
|
if ($e instanceof Exception) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function execute();
|
/// public static methods //////////////////////////////////////////////////
|
||||||
|
|
||||||
public function destroy() {
|
/**
|
||||||
$this->delete_convertdir();
|
* Makes sure that this converter is available at this site
|
||||||
|
*
|
||||||
|
* This is intended for eventual PHP extensions check, environment check etc.
|
||||||
|
* All checks that do not depend on actual backup data should be done here.
|
||||||
|
*
|
||||||
|
* @return boolean true if this converter should be considered as available
|
||||||
|
*/
|
||||||
|
public static function is_available() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the format of the backup directory
|
||||||
|
*
|
||||||
|
* Moodle 2.x format is being detected by the core itself. The converters are
|
||||||
|
* therefore supposed to detect the source format. Eventually, if the target
|
||||||
|
* format os not {@link backup::FORMAT_MOODLE} then they should be able to
|
||||||
|
* detect both source and target formats.
|
||||||
|
*
|
||||||
|
* @param string $tempdir the name of the backup directory
|
||||||
|
* @return null|string null if not recognized, backup::FORMAT_xxx otherwise
|
||||||
|
*/
|
||||||
|
public static function detect_format($tempdir) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the basic information about the converter
|
||||||
|
*
|
||||||
|
* The returned array must contain the following keys:
|
||||||
|
* 'from' - the supported source format, eg. backup::FORMAT_MOODLE1
|
||||||
|
* 'to' - the supported target format, eg. backup::FORMAT_MOODLE
|
||||||
|
* 'cost' - the cost of the conversion, non-negative non-zero integer
|
||||||
|
*/
|
||||||
|
public static function description() {
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'from' => null,
|
||||||
|
'to' => null,
|
||||||
|
'cost' => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// end of public API //////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the instance if needed, called by the constructor
|
||||||
|
*/
|
||||||
|
protected function init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the contents of the tempdir into the target format in the workdir
|
||||||
|
*/
|
||||||
|
protected abstract function execute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the full path to the working directory
|
||||||
|
*/
|
||||||
|
protected function get_workdir_path() {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
return "$CFG->dataroot/temp/backup/$this->workdir";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the full path to the directory with the source backup
|
||||||
|
*/
|
||||||
|
protected function get_tempdir_path() {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
return "$CFG->dataroot/temp/backup/$this->tempdir";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a new empty working directory
|
||||||
|
*/
|
||||||
|
protected function create_workdir() {
|
||||||
|
|
||||||
|
fulldelete($this->get_workdir_path());
|
||||||
|
if (!check_dir_exists($this->get_workdir_path())) {
|
||||||
|
throw new backup_exception('failedtocreateworkdir');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the source backup directory with the converted version
|
||||||
|
*
|
||||||
|
* If $CFG->keeptempdirectoriesonbackup is defined, the original source
|
||||||
|
* source backup directory is kept for debugging purposes.
|
||||||
|
*/
|
||||||
|
protected function replace_tempdir() {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
if (empty($CFG->keeptempdirectoriesonbackup)) {
|
||||||
|
fulldelete($this->get_tempdir_path);
|
||||||
|
} else {
|
||||||
|
if (!rename($this->get_tempdir_path, $this->get_tempdir_path . '_' . $this->get_name() . '_' . $this->id . '_source')) {
|
||||||
|
throw new backup_exception('failedrenamesourcetempdir');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rename($this->get_workdir_path(), $this->get_tempdir_path())) {
|
||||||
|
throw new backup_exception('failedmoveconvertedintoplace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up stuff after the execution
|
||||||
|
*
|
||||||
|
* Note that we do not know if the execution was successful or not.
|
||||||
|
* An exception might have been thrown.
|
||||||
|
*/
|
||||||
|
protected function destroy() {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
if (empty($CFG->keeptempdirectoriesonbackup)) {
|
||||||
|
fulldelete($this->get_workdir_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,40 +25,31 @@
|
|||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plan based abstract converter
|
* Base class for all converters using plan/tasks/steps pattern
|
||||||
*
|
*
|
||||||
* All backup converters that use {@link convert_plan} should extend this class.
|
* All converters that use {@link convert_plan} must extend this class.
|
||||||
*/
|
*/
|
||||||
abstract class plan_converter extends base_converter {
|
abstract class planned_converter extends base_converter {
|
||||||
|
|
||||||
/**
|
/** @var convert_plan */
|
||||||
* @var convert_plan
|
|
||||||
*/
|
|
||||||
protected $plan;
|
protected $plan;
|
||||||
|
/** @var progressive_parser */
|
||||||
/**
|
|
||||||
* @var progressive_parser
|
|
||||||
*/
|
|
||||||
protected $xmlparser;
|
protected $xmlparser;
|
||||||
|
/** @var convert_structure_parser_processor */
|
||||||
/**
|
|
||||||
* @var convert_structure_parser_processor
|
|
||||||
*/
|
|
||||||
protected $xmlprocessor;
|
protected $xmlprocessor;
|
||||||
|
/** @var array path elements to process */
|
||||||
|
protected $pathelements = array();
|
||||||
|
/** @todo needed? redo? path currently locking processing of children */
|
||||||
|
protected $pathlock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* Instructs the dispatcher to ignore all children below path processor returning it
|
||||||
*/
|
*/
|
||||||
protected $pathelements = array(); // Array of pathelements to process
|
const SKIP_ALL_CHILDREN = -991399;
|
||||||
|
|
||||||
// @todo needed? redo?
|
|
||||||
protected $pathlock; // Path currently locking processing of children
|
|
||||||
|
|
||||||
// @todo IDK what this is really...
|
|
||||||
const SKIP_ALL_CHILDREN = -991399; // To instruct the dispatcher about to ignore
|
|
||||||
// all children below path processor returning it
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Return the plan instance, instatinate it if it does not exist yet
|
||||||
|
*
|
||||||
* @return convert_plan
|
* @return convert_plan
|
||||||
*/
|
*/
|
||||||
public function get_plan() {
|
public function get_plan() {
|
||||||
|
@ -56,23 +56,6 @@ abstract class convert_factory {
|
|||||||
return new $classname($tempdir);
|
return new $classname($tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a list of all installed converters operating on a given directory
|
|
||||||
*
|
|
||||||
* @param string $tempdir The temp directory to operate on
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function converters($tempdir) {
|
|
||||||
global $CFG;
|
|
||||||
|
|
||||||
$converters = array();
|
|
||||||
$plugins = get_list_of_plugins('backup/converter');
|
|
||||||
foreach ($plugins as $name) {
|
|
||||||
$converters[$name] = self::converter($name, $tempdir);
|
|
||||||
}
|
|
||||||
return $converters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs through all plugins of a specific type and instantiates their task class
|
* Runs through all plugins of a specific type and instantiates their task class
|
||||||
*
|
*
|
||||||
|
@ -228,42 +228,34 @@ abstract class backup_general_helper extends backup_helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given one temp/backup/xxx dir, detect its format
|
* Detects the format of the given unpacked backup directory
|
||||||
*
|
*
|
||||||
* TODO: Move harcoded detection here to delegated classes under backup/format (moodle1, imscc..)
|
* @param string $tempdir the name of the backup directory
|
||||||
* conversion code will be there too.
|
* @return string one of backup::FORMAT_xxx constants
|
||||||
*/
|
*/
|
||||||
public static function detect_backup_format($tempdir) {
|
public static function detect_backup_format($tempdir) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
$filepath = $CFG->dataroot . '/temp/backup/' . $tempdir . '/moodle_backup.xml';
|
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||||
|
|
||||||
// Does tempdir exist and is dir
|
if (convert_helper::detect_moodle2_format($tempdir)) {
|
||||||
if (!is_dir(dirname($filepath))) {
|
|
||||||
throw new backup_helper_exception('tmp_backup_directory_not_found', dirname($filepath));
|
|
||||||
}
|
|
||||||
|
|
||||||
// First look for MOODLE (moodle2) format
|
|
||||||
if (file_exists($filepath)) { // Looks promising, lets load some information
|
|
||||||
$handle = fopen ($filepath, "r");
|
|
||||||
$first_chars = fread($handle,200);
|
|
||||||
$status = fclose ($handle);
|
|
||||||
// Check if it has the required strings
|
|
||||||
if (strpos($first_chars,'<?xml version="1.0" encoding="UTF-8"?>') !== false &&
|
|
||||||
strpos($first_chars,'<moodle_backup>') !== false &&
|
|
||||||
strpos($first_chars,'<information>') !== false) {
|
|
||||||
return backup::FORMAT_MOODLE;
|
return backup::FORMAT_MOODLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see if a converter can identify the format
|
||||||
|
$converters = convert_factory::available_converters();
|
||||||
|
foreach ($converters as $name) {
|
||||||
|
$classname = "{$name}_converter";
|
||||||
|
if (!class_exists($classname)) {
|
||||||
|
throw new coding_exception("available_converters() is supposed to load
|
||||||
|
converter classes but class $classname not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if a converter can identify the format as its own
|
$detected = call_user_func($classname .'::detect_format', $tempdir);
|
||||||
$converters = convert_factory::converters($tempdir);
|
if (!empty($detected)) {
|
||||||
foreach ($converters as $name => $converter) {
|
return $detected;
|
||||||
if ($converter->can_convert()) {
|
|
||||||
return $name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrived here, unknown format
|
|
||||||
return backup::FORMAT_UNKNOWN;
|
return backup::FORMAT_UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Provides {@link convert_helper} class
|
||||||
|
*
|
||||||
* @package core
|
* @package core
|
||||||
* @subpackage backup-convert
|
* @subpackage backup-convert
|
||||||
* @copyright 2011 Mark Nielsen <mark@moodlerooms.com>
|
* @copyright 2011 Mark Nielsen <mark@moodlerooms.com>
|
||||||
@ -25,37 +27,129 @@
|
|||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General convert helper
|
* Provides various functionality via its static methods
|
||||||
*/
|
*/
|
||||||
abstract class convert_helper {
|
abstract class convert_helper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $entropy
|
||||||
|
* @return string random identifier
|
||||||
|
*/
|
||||||
public static function generate_id($entropy) {
|
public static function generate_id($entropy) {
|
||||||
return md5(time() . '-' . $entropy . '-' . random_string(20));
|
return md5(time() . '-' . $entropy . '-' . random_string(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @static
|
* Returns the list of all available converters and loads their classes
|
||||||
|
*
|
||||||
|
* Converter must be installed as a directory in backup/converter/ and its
|
||||||
|
* method is_available() must return true to get to the list.
|
||||||
|
*
|
||||||
|
* @see base_converter::is_available()
|
||||||
|
* @return array of strings
|
||||||
|
*/
|
||||||
|
public static function available_converters() {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
$converters = array();
|
||||||
|
$plugins = get_list_of_plugins('backup/converter');
|
||||||
|
foreach ($plugins as $name) {
|
||||||
|
$classfile = "$CFG->dirroot/backup/converter/$name/converter.class.php";
|
||||||
|
$classname = "{$name}_converter";
|
||||||
|
|
||||||
|
if (!file_exists($classfile)) {
|
||||||
|
throw new coding_exception("Converter factory error: class file not found $classfile");
|
||||||
|
}
|
||||||
|
require_once($classfile);
|
||||||
|
|
||||||
|
if (!class_exists($classname)) {
|
||||||
|
throw new coding_exception("Converter factory error: class not found $classname");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (call_user_func($classname .'::is_available')) {
|
||||||
|
$converters[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if the given folder contains an unpacked moodle2 backup
|
||||||
|
*
|
||||||
|
* @param string $tempdir the name of the backup directory
|
||||||
|
* @return boolean true if moodle2 format detected, false otherwise
|
||||||
|
*/
|
||||||
|
public static function detect_moodle2_format($tempdir) {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
$dirpath = $CFG->dataroot . '/temp/backup/' . $tempdir;
|
||||||
|
$filepath = $dirpath . '/moodle_backup.xml';
|
||||||
|
|
||||||
|
if (!is_dir($dirpath)) {
|
||||||
|
throw new backup_helper_exception('tmp_backup_directory_not_found', $dirpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = fopen($filepath, 'r');
|
||||||
|
$firstchars = fread($handle, 200);
|
||||||
|
$status = fclose($handle);
|
||||||
|
|
||||||
|
if (strpos($firstchars,'<?xml version="1.0" encoding="UTF-8"?>') !== false and
|
||||||
|
strpos($firstchars,'<moodle_backup>') !== false and
|
||||||
|
strpos($firstchars,'<information>') !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given directory with the backup into moodle2 format
|
||||||
|
*
|
||||||
* @throws coding_exception|restore_controller_exception
|
* @throws coding_exception|restore_controller_exception
|
||||||
* @param string $tempdir The directory to convert
|
* @param string $tempdir The directory to convert
|
||||||
* @param string $format The current format, if already detected
|
* @param string $format The current format, if already detected
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function to_moodle2_format($tempdir, $format = NULL) {
|
public static function to_moodle2_format($tempdir, $format = null) {
|
||||||
|
|
||||||
if (is_null($format)) {
|
if (is_null($format)) {
|
||||||
$format = backup_general_helper::detect_backup_format($tempdir);
|
$format = backup_general_helper::detect_backup_format($tempdir);
|
||||||
}
|
}
|
||||||
while (!in_array($format, array(backup::FORMAT_MOODLE, backup::FORMAT_UNKNOWN))) {
|
|
||||||
$converter = convert_factory::converter($format, $tempdir);
|
|
||||||
|
|
||||||
if (!$converter->can_convert()) {
|
// get the supported conversion paths from all available converters
|
||||||
throw new coding_exception('Converter detection failed, the loaded converter cannot convert this format');
|
$converters = convert_factory::available_converters();
|
||||||
|
$descriptions = array();
|
||||||
|
foreach ($converters as $name) {
|
||||||
|
$classname = "{$name}_converter";
|
||||||
|
if (!class_exists($classname)) {
|
||||||
|
throw new coding_exception("available_converters() is supposed to load
|
||||||
|
converter classes but class $classname not found");
|
||||||
}
|
}
|
||||||
|
$descriptions[$name] = call_user_func($classname .'::description');
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose the best conversion path for the given format
|
||||||
|
$path = self::choose_conversion_path($format, $descriptions);
|
||||||
|
|
||||||
|
if (empty($path)) {
|
||||||
|
// unable to convert
|
||||||
|
// todo throwing exception is not a good way to control the flow here
|
||||||
|
throw new coding_exception('Unable to find conversion path');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($path as $name) {
|
||||||
|
$converter = convert_factory::converter($name, $tempdir);
|
||||||
$converter->convert();
|
$converter->convert();
|
||||||
|
|
||||||
// Re-detect format
|
|
||||||
$format = backup_general_helper::detect_backup_format($tempdir);
|
|
||||||
}
|
}
|
||||||
if ($format == backup::FORMAT_UNKNOWN) {
|
|
||||||
throw new restore_controller_exception('cannot_convert_from_unknown_format'); // @todo Change exception class
|
// make sure we ended with moodle2 format
|
||||||
|
if (!self::detect_moodle2_format($tempdir)) {
|
||||||
|
throw new coding_exception('Conversion failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +158,6 @@ abstract class convert_helper {
|
|||||||
*/
|
*/
|
||||||
public static function set_inforef($contextid) {
|
public static function set_inforef($contextid) {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_inforef($contextid) {
|
public static function get_inforef($contextid) {
|
||||||
@ -122,4 +214,143 @@ abstract class convert_helper {
|
|||||||
throw new Exception(sprintf("Could not insert context record into temp table: %s", $msg));
|
throw new Exception(sprintf("Could not insert context record into temp table: %s", $msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// end of public API //////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose the best conversion path for the given format
|
||||||
|
*
|
||||||
|
* Given the source format and the list of available converters and their properties,
|
||||||
|
* this methods picks the most effective way how to convert the source format into
|
||||||
|
* the target moodle2 format. The method returns a list of converters that should be
|
||||||
|
* called, in order.
|
||||||
|
*
|
||||||
|
* This implementation uses Dijkstra's algorithm to find the shortest way through
|
||||||
|
* the oriented graph.
|
||||||
|
*
|
||||||
|
* @see http://en.wikipedia.org/wiki/Dijkstra's_algorithm
|
||||||
|
* @param string $format the source backup format, one of backup::FORMAT_xxx
|
||||||
|
* @param array $descriptions list of {@link base_converter::description()} indexed by the converter name
|
||||||
|
* @return array ordered list of converter names to call (may be empty if not reachable)
|
||||||
|
*/
|
||||||
|
protected static function choose_conversion_path($format, array $descriptions) {
|
||||||
|
|
||||||
|
// construct an oriented graph of conversion paths. backup formats are nodes
|
||||||
|
// and the the converters are edges of the graph.
|
||||||
|
$paths = array(); // [fromnode][tonode] => converter
|
||||||
|
foreach ($descriptions as $converter => $description) {
|
||||||
|
$from = $description['from'];
|
||||||
|
$to = $description['to'];
|
||||||
|
$cost = $description['cost'];
|
||||||
|
|
||||||
|
if (is_null($from) or $from === backup::FORMAT_UNKNOWN or
|
||||||
|
is_null($to) or $to === backup::FORMAT_UNKNOWN or
|
||||||
|
is_null($cost) or $cost <= 0) {
|
||||||
|
throw new coding_exception('Invalid converter description:' . $converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($paths[$from][$to])) {
|
||||||
|
$paths[$from][$to] = $converter;
|
||||||
|
} else {
|
||||||
|
// if there are two converters available for the same conversion
|
||||||
|
// path, choose the one with the lowest cost. if there are more
|
||||||
|
// available converters with the same cost, the chosen one is
|
||||||
|
// undefined (depends on the order of processing)
|
||||||
|
if ($descriptions[$paths[$from][$to]]['cost'] > $cost) {
|
||||||
|
$paths[$from][$to] = $converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($paths)) {
|
||||||
|
// no conversion paths available
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// now use Dijkstra's algorithm and find the shortest conversion path
|
||||||
|
|
||||||
|
$dist = array(); // list of nodes and their distances from the source format
|
||||||
|
$prev = array(); // list of previous nodes in optimal path from the source format
|
||||||
|
foreach ($paths as $fromnode => $tonodes) {
|
||||||
|
$dist[$fromnode] = null; // infinitive distance, can't be reached
|
||||||
|
$prev[$fromnode] = null; // unknown
|
||||||
|
foreach ($tonodes as $tonode => $converter) {
|
||||||
|
$dist[$tonode] = null; // infinitive distance, can't be reached
|
||||||
|
$prev[$tonode] = null; // unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists($format, $dist)) {
|
||||||
|
return array();
|
||||||
|
} else {
|
||||||
|
$dist[$format] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$queue = array_flip(array_keys($dist));
|
||||||
|
while (!empty($queue)) {
|
||||||
|
// find the node with the smallest distance from the source in the queue
|
||||||
|
// in the first iteration, this will find the original format node itself
|
||||||
|
$closest = null;
|
||||||
|
foreach ($queue as $node => $undefined) {
|
||||||
|
if (is_null($dist[$node])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_null($closest) or ($dist[$node] < $dist[$closest])) {
|
||||||
|
$closest = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($closest) or is_null($dist[$closest])) {
|
||||||
|
// all remaining nodes are inaccessible from source
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($closest === backup::FORMAT_MOODLE) {
|
||||||
|
// bingo we can break now
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($queue[$closest]);
|
||||||
|
|
||||||
|
// visit all neighbors and update distances to them eventually
|
||||||
|
|
||||||
|
if (!isset($paths[$closest])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$neighbors = array_keys($paths[$closest]);
|
||||||
|
// keep just neighbors that are in the queue yet
|
||||||
|
foreach ($neighbors as $ix => $neighbor) {
|
||||||
|
if (!array_key_exists($neighbor, $queue)) {
|
||||||
|
unset($neighbors[$ix]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($neighbors as $neighbor) {
|
||||||
|
// the alternative distance to the neighbor if we went thru the
|
||||||
|
// current $closest node
|
||||||
|
$alt = $dist[$closest] + $descriptions[$paths[$closest][$neighbor]]['cost'];
|
||||||
|
|
||||||
|
if (is_null($dist[$neighbor]) or $alt < $dist[$neighbor]) {
|
||||||
|
// we found a shorter way to the $neighbor, remember it
|
||||||
|
$dist[$neighbor] = $alt;
|
||||||
|
$prev[$neighbor] = $closest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($dist[backup::FORMAT_MOODLE])) {
|
||||||
|
// unable to find a conversion path, the target format not reachable
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconstruct the optimal path from the source format to the target one
|
||||||
|
$conversionpath = array();
|
||||||
|
$target = backup::FORMAT_MOODLE;
|
||||||
|
while (isset($prev[$target])) {
|
||||||
|
array_unshift($conversionpath, $paths[$prev[$target]][$target]);
|
||||||
|
$target = $prev[$target];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conversionpath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
146
backup/util/helper/simpletest/testconverthelper.php
Normal file
146
backup/util/helper/simpletest/testconverthelper.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for backup/util/helper/convert_helper.class.php
|
||||||
|
*
|
||||||
|
* @package core
|
||||||
|
* @subpackage backup-convert
|
||||||
|
* @copyright 2011 David Mudrak <david@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the protected methods we need to test
|
||||||
|
*/
|
||||||
|
class testable_convert_helper extends convert_helper {
|
||||||
|
|
||||||
|
public static function choose_conversion_path($format, array $descriptions) {
|
||||||
|
return parent::choose_conversion_path($format, $descriptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the test methods
|
||||||
|
*/
|
||||||
|
class convert_helper_test extends UnitTestCase {
|
||||||
|
|
||||||
|
public static $includecoverage = array();
|
||||||
|
|
||||||
|
public function test_choose_conversion_path() {
|
||||||
|
|
||||||
|
// no converters available
|
||||||
|
$descriptions = array();
|
||||||
|
$path = testable_convert_helper::choose_conversion_path(backup::FORMAT_MOODLE1, $descriptions);
|
||||||
|
$this->assertEqual($path, array());
|
||||||
|
|
||||||
|
// missing source and/or targets
|
||||||
|
$descriptions = array(
|
||||||
|
// some custom converter
|
||||||
|
'exporter' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => 'some_custom_format',
|
||||||
|
'cost' => 10,
|
||||||
|
),
|
||||||
|
// another custom converter
|
||||||
|
'converter' => array(
|
||||||
|
'from' => 'yet_another_crazy_custom_format',
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 10,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$path = testable_convert_helper::choose_conversion_path(backup::FORMAT_MOODLE1, $descriptions);
|
||||||
|
$this->assertEqual($path, array());
|
||||||
|
|
||||||
|
$path = testable_convert_helper::choose_conversion_path('some_other_custom_format', $descriptions);
|
||||||
|
$this->assertEqual($path, array());
|
||||||
|
|
||||||
|
// single step conversion
|
||||||
|
$path = testable_convert_helper::choose_conversion_path('yet_another_crazy_custom_format', $descriptions);
|
||||||
|
$this->assertEqual($path, array('converter'));
|
||||||
|
|
||||||
|
// no conversion needed - this is supposed to be detected by the caller
|
||||||
|
$path = testable_convert_helper::choose_conversion_path(backup::FORMAT_MOODLE, $descriptions);
|
||||||
|
$this->assertEqual($path, array());
|
||||||
|
|
||||||
|
// two alternatives
|
||||||
|
$descriptions = array(
|
||||||
|
// standard moodle 1.9 -> 2.x converter
|
||||||
|
'moodle1' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 10,
|
||||||
|
),
|
||||||
|
// alternative moodle 1.9 -> 2.x converter
|
||||||
|
'alternative' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 8,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$path = testable_convert_helper::choose_conversion_path(backup::FORMAT_MOODLE1, $descriptions);
|
||||||
|
$this->assertEqual($path, array('alternative'));
|
||||||
|
|
||||||
|
// complex case
|
||||||
|
$descriptions = array(
|
||||||
|
// standard moodle 1.9 -> 2.x converter
|
||||||
|
'moodle1' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 10,
|
||||||
|
),
|
||||||
|
// alternative moodle 1.9 -> 2.x converter
|
||||||
|
'alternative' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 8,
|
||||||
|
),
|
||||||
|
// custom converter from 1.9 -> custom 'CFv1' format
|
||||||
|
'cc1' => array(
|
||||||
|
'from' => backup::FORMAT_MOODLE1,
|
||||||
|
'to' => 'CFv1',
|
||||||
|
'cost' => 2,
|
||||||
|
),
|
||||||
|
// custom converter from custom 'CFv1' format -> moodle 2.0 format
|
||||||
|
'cc2' => array(
|
||||||
|
'from' => 'CFv1',
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 5,
|
||||||
|
),
|
||||||
|
// custom converter from CFv1 -> CFv2 format
|
||||||
|
'cc3' => array(
|
||||||
|
'from' => 'CFv1',
|
||||||
|
'to' => 'CFv2',
|
||||||
|
'cost' => 2,
|
||||||
|
),
|
||||||
|
// custom converter from CFv2 -> moodle 2.0 format
|
||||||
|
'cc4' => array(
|
||||||
|
'from' => 'CFv2',
|
||||||
|
'to' => backup::FORMAT_MOODLE,
|
||||||
|
'cost' => 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ask the helper to find the most effective way
|
||||||
|
$path = testable_convert_helper::choose_conversion_path(backup::FORMAT_MOODLE1, $descriptions);
|
||||||
|
$this->assertEqual($path, array('cc1', 'cc3', 'cc4'));
|
||||||
|
}
|
||||||
|
}
|
@ -48,7 +48,7 @@ class convert_plan extends base_plan implements loggable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_basepath() {
|
public function get_basepath() {
|
||||||
return $this->converter->get_convertdir();
|
return $this->converter->get_workdir_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,6 +30,7 @@ defined('MOODLE_INTERNAL') || die();
|
|||||||
* Convert forum
|
* Convert forum
|
||||||
*/
|
*/
|
||||||
class moodle1_forum_activity_structure_step extends convert_structure_step {
|
class moodle1_forum_activity_structure_step extends convert_structure_step {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will return the structure to be processed by this convert_step.
|
* Function that will return the structure to be processed by this convert_step.
|
||||||
* Must return one array of @convert_path_element elements
|
* Must return one array of @convert_path_element elements
|
||||||
|
Loading…
x
Reference in New Issue
Block a user