MDL-27376 MDL-27377 MDL-27378 Backup converters refactoring - work in progress
These are David's changes of Mark's code that replace the plan/tasks/steps infrastructure with a bit simpler one. The changes will be described in the next commit that will actually finish the conversion. TODO: refactor backup/converter/moodle1/stepslib.php into conversion handlers.
This commit is contained in:
@@ -381,14 +381,18 @@ class restore_controller extends backup implements loggable {
|
||||
*/
|
||||
public function convert() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
|
||||
|
||||
if ($this->status != backup::STATUS_REQUIRE_CONV) {
|
||||
throw new restore_controller_exception('cannot_convert_not_required_status');
|
||||
}
|
||||
|
||||
// Run conversion to the proper format
|
||||
convert_helper::to_moodle2_format($this->get_tempdir(), $this->format);
|
||||
if (!convert_helper::to_moodle2_format($this->get_tempdir(), $this->format)) {
|
||||
// todo - unable to find the conversion path, what to do now?
|
||||
// throwing the exception as a temporary solution
|
||||
throw new restore_controller_exception('unable_to_find_conversion_path');
|
||||
}
|
||||
|
||||
// If no exceptions were thrown, then we are in the proper format
|
||||
$this->format = backup::FORMAT_MOODLE;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Provides base converter classes
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <mark@moodlerooms.com>
|
||||
@@ -24,10 +26,14 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||
|
||||
/**
|
||||
* Base abstract converter
|
||||
* Base converter class
|
||||
*
|
||||
* @throws backup_exception|Exception|null
|
||||
* All Moodle backup converters are supposed to extend this base class.
|
||||
*
|
||||
* @throws convert_exception
|
||||
*/
|
||||
abstract class base_converter {
|
||||
|
||||
@@ -74,9 +80,6 @@ abstract class base_converter {
|
||||
*/
|
||||
public function convert() {
|
||||
|
||||
$e = null;
|
||||
|
||||
// try to execute the converter
|
||||
try {
|
||||
$this->create_workdir();
|
||||
$this->execute();
|
||||
@@ -88,7 +91,7 @@ abstract class base_converter {
|
||||
$this->destroy();
|
||||
|
||||
// eventually re-throw the execution exception
|
||||
if ($e instanceof Exception) {
|
||||
if (isset($e) and ($e instanceof Exception)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -139,6 +142,24 @@ abstract class base_converter {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the full path to the working directory
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public function get_tempdir_path() {
|
||||
global $CFG;
|
||||
|
||||
return "$CFG->dataroot/temp/backup/$this->tempdir";
|
||||
}
|
||||
|
||||
/// end of public API //////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@@ -152,24 +173,6 @@ abstract class base_converter {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -177,7 +180,7 @@ abstract class base_converter {
|
||||
|
||||
fulldelete($this->get_workdir_path());
|
||||
if (!check_dir_exists($this->get_workdir_path())) {
|
||||
throw new backup_exception('failedtocreateworkdir');
|
||||
throw new convert_exception('failed_create_workdir');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,15 +194,15 @@ abstract class base_converter {
|
||||
global $CFG;
|
||||
|
||||
if (empty($CFG->keeptempdirectoriesonbackup)) {
|
||||
fulldelete($this->get_tempdir_path);
|
||||
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_tempdir_path(), $this->get_tempdir_path() . '_' . $this->get_name() . '_' . $this->id . '_source')) {
|
||||
throw new convert_exception('failed_rename_source_tempdir');
|
||||
}
|
||||
}
|
||||
|
||||
if (!rename($this->get_workdir_path(), $this->get_tempdir_path())) {
|
||||
throw new backup_exception('failedmoveconvertedintoplace');
|
||||
throw new convert_exception('failed_move_converted_into_place');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +216,26 @@ abstract class base_converter {
|
||||
global $CFG;
|
||||
|
||||
if (empty($CFG->keeptempdirectoriesonbackup)) {
|
||||
fulldelete($this->get_workdir_path);
|
||||
fulldelete($this->get_workdir_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General convert-related exception
|
||||
*
|
||||
* @author David Mudrak <david@moodle.com>
|
||||
*/
|
||||
class convert_exception extends moodle_exception {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode key for the corresponding error string
|
||||
* @param object $a extra words and phrases that might be required in the error string
|
||||
* @param string $debuginfo optional debugging information
|
||||
*/
|
||||
public function __construct($errorcode, $a = null, $debuginfo = null) {
|
||||
parent::__construct($errorcode, '', '', $a, $debuginfo);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/converter/moodle1/taskslib.php');
|
||||
require_once($CFG->dirroot . '/backup/converter/moodle1/stepslib.php');
|
||||
|
||||
/**
|
||||
* Converter of Moodle 1.9 backup into Moodle 2.x format
|
||||
*/
|
||||
class moodle1_converter extends plan_converter {
|
||||
|
||||
/** @var string the current module being processed */
|
||||
protected $currentmod = '';
|
||||
|
||||
/** @var string the current block being processed */
|
||||
protected $currentblock = '';
|
||||
|
||||
/**
|
||||
* 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 static function detect_format($tempdir) {
|
||||
global $CFG;
|
||||
|
||||
$filepath = $CFG->dataroot . '/temp/backup/' . $tempdir . '/moodle.xml';
|
||||
if (file_exists($filepath)) {
|
||||
// looks promising, lets load some information
|
||||
$handle = fopen($filepath, 'r');
|
||||
$first_chars = fread($handle, 200);
|
||||
fclose($handle);
|
||||
|
||||
// check if it has the required strings
|
||||
if (strpos($first_chars,'<?xml version="1.0" encoding="UTF-8"?>') !== false and
|
||||
strpos($first_chars,'<MOODLE_BACKUP>') !== false and
|
||||
strpos($first_chars,'<INFO>') !== false) {
|
||||
|
||||
return backup::FORMAT_MOODLE1;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path transformation for modules and blocks. Here we
|
||||
* are collapsing paths that use the plugin's name.
|
||||
*/
|
||||
public function add_structures($processingobject, array $structures) {
|
||||
parent::add_structures($processingobject, $structures);
|
||||
|
||||
foreach ($structures as $element) {
|
||||
$path = $element->get_path();
|
||||
|
||||
// @todo Add same for blocks
|
||||
$path = preg_replace('/^\/MOODLE_BACKUP\/COURSE\/MODULES\/MOD\/(\w+)\//', '/MOODLE_BACKUP/COURSE/MODULES/MOD/', $path);
|
||||
if (!empty($path) and $path != $element->get_path()) {
|
||||
$this->xmlprocessor->add_path($path, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Path transformation for modules and blocks. Here we
|
||||
* are expanding paths to include the plugin's name.
|
||||
*/
|
||||
public function process($data) {
|
||||
$path = $data['path'];
|
||||
|
||||
// @todo Same path manipulation for blocks
|
||||
if ($path == '/MOODLE_BACKUP/COURSE/MODULES/MOD') {
|
||||
$this->currentmod = strtoupper($data['tags']['MODTYPE']);
|
||||
$path = '/MOODLE_BACKUP/COURSE/MODULES/MOD/'.$this->currentmod;
|
||||
|
||||
} else if (strpos($path, '/MOODLE_BACKUP/COURSE/MODULES/MOD') === 0) {
|
||||
$path = str_replace('/MOODLE_BACKUP/COURSE/MODULES/MOD', '/MOODLE_BACKUP/COURSE/MODULES/MOD/'.$this->currentmod, $path);
|
||||
}
|
||||
if ($path != $data['path']) {
|
||||
// Have relaxed error handling on path transformations...
|
||||
if (!array_key_exists($path, $this->pathelements)) {
|
||||
debugging("Path transformation error, $path is not registered, probably similar to another plugin");
|
||||
return;
|
||||
}
|
||||
$data['path'] = $path;
|
||||
}
|
||||
parent::process($data);
|
||||
}
|
||||
|
||||
public function build_plan() {
|
||||
$this->xmlparser = new progressive_parser();
|
||||
$this->xmlparser->set_file($this->get_tempdir_path() . '/moodle.xml');
|
||||
$this->xmlprocessor = new convert_structure_parser_processor($this); // @todo Probably move this
|
||||
$this->xmlparser->set_processor($this->xmlprocessor);
|
||||
|
||||
// These paths are dispatched by the converter through path transformation
|
||||
$this->xmlprocessor->add_path('/MOODLE_BACKUP/COURSE/MODULES/MOD', false);
|
||||
// @todo Add the same for blocks
|
||||
|
||||
$this->get_plan()->add_task(new moodle1_root_task('root_task'));
|
||||
$this->get_plan()->add_task(new moodle1_course_task('courseinfo'));
|
||||
|
||||
// Build plugin tasks
|
||||
convert_factory::build_plugin_tasks($this, 'mod', 'activity');
|
||||
convert_factory::build_plugin_tasks($this, 'block');
|
||||
|
||||
$this->get_plan()->add_task(new moodle1_final_task('final_task'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defines Moodle 1.9 backup conversion handlers
|
||||
*
|
||||
* Handlers are classes responsible for the actual conversion work. Their logic
|
||||
* is similar to the functionality provided by steps in plan based restore process.
|
||||
*
|
||||
* @package backup-convert
|
||||
* @subpackage moodle1
|
||||
* @copyright 2011 David Mudrak <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/xml/xml_writer.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/xml/output/xml_output.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/xml/output/file_xml_output.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/dbops/backup_dbops.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/dbops/backup_controller_dbops.class.php');
|
||||
|
||||
/**
|
||||
* Handlers factory class
|
||||
*/
|
||||
abstract class moodle1_handlers_factory {
|
||||
|
||||
/**
|
||||
* @param moodle1_converter the converter requesting the converters
|
||||
* @return list of all available conversion handlers
|
||||
*/
|
||||
public static function get_handlers(moodle1_converter $converter) {
|
||||
|
||||
$handlers = array();
|
||||
$handlers[] = new moodle1_root_handler($converter);
|
||||
|
||||
$handlers = array_merge($handlers, self::get_plugin_handlers('mod', $converter));
|
||||
$handlers = array_merge($handlers, self::get_plugin_handlers('block', $converter));
|
||||
|
||||
return $handlers;
|
||||
}
|
||||
|
||||
/// public API ends here ///////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Runs through all plugins of a specific type and instantiates their handlers
|
||||
*
|
||||
* @todo ask mod's subplugins
|
||||
* @param string $type the plugin type
|
||||
* @param moodle1_converter $converter the converter requesting the handler
|
||||
* @throws convert_exception
|
||||
* @return array of {@link moodle1_handler} instances
|
||||
*/
|
||||
public static function get_plugin_handlers($type, moodle1_converter $converter) {
|
||||
global $CFG;
|
||||
|
||||
$handlers = array();
|
||||
$plugins = get_plugin_list($type);
|
||||
foreach ($plugins as $name => $dir) {
|
||||
$handlerfile = $dir . '/backup/moodle1/lib.php';
|
||||
$handlerclass = "moodle1_{$type}_{$name}_handler";
|
||||
if (!file_exists($handlerfile)) {
|
||||
continue;
|
||||
}
|
||||
require_once($handlerfile);
|
||||
|
||||
if (!class_exists($handlerclass)) {
|
||||
throw new convert_exception('missing_handler_class', $handlerclass);
|
||||
}
|
||||
$handlers[] = new $handlerclass($converter, $type, $name);
|
||||
}
|
||||
return $handlers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General backup conversion handler
|
||||
*/
|
||||
abstract class moodle1_handler {
|
||||
|
||||
/** @var moodle1_converter */
|
||||
protected $converter;
|
||||
|
||||
/** @var xml_writer */
|
||||
protected $xmlwriter;
|
||||
|
||||
/**
|
||||
* @param moodle1_converter $converter the converter that requires us
|
||||
*/
|
||||
public function __construct(moodle1_converter $converter) {
|
||||
|
||||
$this->converter = $converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the XML writer - after calling, one is free to use $xmlwriter
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function open_xml_writer() {
|
||||
|
||||
if (is_null($this->get_xml_filename())) {
|
||||
throw new convert_exception('handler_not_expected_to_write_xml');
|
||||
}
|
||||
|
||||
if (!$this->xmlwriter instanceof xml_writer) {
|
||||
$fullpath = $this->converter->get_workdir_path() . '/' . $this->get_xml_filename();
|
||||
$directory = pathinfo($fullpath, PATHINFO_DIRNAME);
|
||||
|
||||
if (!check_dir_exists($directory)) {
|
||||
throw new convert_exception('unable_create_target_directory', $directory);
|
||||
}
|
||||
$this->xmlwriter = new xml_writer(new file_xml_output($fullpath));
|
||||
$this->xmlwriter->start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the XML writer
|
||||
*
|
||||
* At the moment, the caller must close all tags before calling
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close_xml_writer() {
|
||||
if ($this->xmlwriter instanceof xml_writer) {
|
||||
$this->xmlwriter->stop();
|
||||
unset($this->xmlwriter);
|
||||
$this->xmlwriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the file name of the target XML file to write into
|
||||
*/
|
||||
abstract protected function get_xml_filename();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shared base class for activity modules and blocks handlers
|
||||
*/
|
||||
abstract class moodle1_plugin_handler extends moodle1_handler {
|
||||
|
||||
/** @var string */
|
||||
protected $plugintype;
|
||||
|
||||
/** @var string */
|
||||
protected $pluginname;
|
||||
|
||||
/**
|
||||
* @param moodle1_converter $converter the converter that requires us
|
||||
* @param string plugintype
|
||||
* @param string pluginname
|
||||
*/
|
||||
public function __construct(moodle1_converter $converter, $plugintype, $pluginname) {
|
||||
|
||||
parent::__construct($converter);
|
||||
$this->plugintype = $plugintype;
|
||||
$this->pluginname = $pluginname;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for activity module handlers
|
||||
*/
|
||||
abstract class moodle1_mod_handler extends moodle1_plugin_handler {
|
||||
|
||||
/** @var int module id */
|
||||
protected $moduleid;
|
||||
|
||||
/**
|
||||
* Return the relative path to the XML file that
|
||||
* this step writes out to. Example: course/course.xml
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_xml_filename() {
|
||||
return "activities/{$this->pluginname}_{$this->moduleid}/module.xml";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for activity module handlers
|
||||
*/
|
||||
abstract class moodle1_block_handler extends moodle1_handler {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the root element of the backup file
|
||||
*/
|
||||
class moodle1_root_handler extends moodle1_handler {
|
||||
|
||||
public function get_paths() {
|
||||
return array(new convert_path('root_element', '/MOODLE_BACKUP'));
|
||||
}
|
||||
|
||||
public function process_root_element($data) {
|
||||
// no data available - nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* This is executed at the very start of the moodle.xml parsing
|
||||
*/
|
||||
public function on_root_element_start() {
|
||||
|
||||
// create ids temp table
|
||||
backup_controller_dbops::create_backup_ids_temp_table($this->converter->get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is executed at the end of the moodle.xml parsing
|
||||
*/
|
||||
public function on_root_element_end() {
|
||||
|
||||
// drop the ids temp table
|
||||
backup_controller_dbops::drop_backup_ids_temp_table($this->converter->get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler does not actually produces any output
|
||||
*/
|
||||
protected function get_xml_filename() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,672 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Provides classes used by the moodle1 converter
|
||||
*
|
||||
* @package backup-convert
|
||||
* @subpackage moodle1
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/converter/convertlib.php');
|
||||
require_once($CFG->dirroot . '/backup/util/xml/parser/progressive_parser.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
|
||||
require_once(dirname(__FILE__) . '/handlerlib.php');
|
||||
|
||||
/**
|
||||
* Converter of Moodle 1.9 backup into Moodle 2.x format
|
||||
*/
|
||||
class moodle1_converter extends base_converter {
|
||||
|
||||
/** @var progressive_parser moodle.xml file parser */
|
||||
protected $xmlparser;
|
||||
|
||||
/** @var moodle1_parser_processor */
|
||||
protected $xmlprocessor;
|
||||
|
||||
/** @var array of {@link convert_path} to process */
|
||||
protected $pathelements = array();
|
||||
|
||||
/** @var string the current module being processed */
|
||||
protected $currentmod = '';
|
||||
|
||||
/** @var string the current block being processed */
|
||||
protected $currentblock = '';
|
||||
|
||||
/** @var string path currently locking processing of children */
|
||||
protected $pathlock;
|
||||
|
||||
/**
|
||||
* Instructs the dispatcher to ignore all children below path processor returning it
|
||||
*/
|
||||
const SKIP_ALL_CHILDREN = -991399;
|
||||
|
||||
/**
|
||||
* 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 static function detect_format($tempdir) {
|
||||
global $CFG;
|
||||
|
||||
$filepath = $CFG->dataroot . '/temp/backup/' . $tempdir . '/moodle.xml';
|
||||
if (file_exists($filepath)) {
|
||||
// looks promising, lets load some information
|
||||
$handle = fopen($filepath, 'r');
|
||||
$first_chars = fread($handle, 200);
|
||||
fclose($handle);
|
||||
|
||||
// check if it has the required strings
|
||||
if (strpos($first_chars,'<?xml version="1.0" encoding="UTF-8"?>') !== false and
|
||||
strpos($first_chars,'<MOODLE_BACKUP>') !== false and
|
||||
strpos($first_chars,'<INFO>') !== false) {
|
||||
|
||||
return backup::FORMAT_MOODLE1;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the instance if needed, called by the constructor
|
||||
*
|
||||
* Here we create objects we need before the execution.
|
||||
*/
|
||||
protected function init() {
|
||||
|
||||
// ask your mother first before going out playing with toys
|
||||
parent::init();
|
||||
|
||||
// good boy, prepare XML parser and processor
|
||||
$this->xmlparser = new progressive_parser();
|
||||
$this->xmlparser->set_file($this->get_tempdir_path() . '/moodle.xml');
|
||||
$this->xmlprocessor = new moodle1_parser_processor($this);
|
||||
$this->xmlparser->set_processor($this->xmlprocessor);
|
||||
|
||||
// make sure that MOD and BLOCK paths are visited
|
||||
$this->xmlprocessor->add_path('/MOODLE_BACKUP/COURSE/MODULES/MOD');
|
||||
$this->xmlprocessor->add_path('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK');
|
||||
|
||||
// register the conversion handlers
|
||||
foreach (moodle1_handlers_factory::get_handlers($this) as $handler) {
|
||||
if (!$handler instanceof moodle1_handler) {
|
||||
throw new convert_exception('wrong_handler_class', get_class($handler));
|
||||
}
|
||||
$this->register_handler($handler, $handler->get_paths());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the contents of the tempdir into the target format in the workdir
|
||||
*/
|
||||
protected function execute() {
|
||||
$this->xmlparser->process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for the given path elements
|
||||
*/
|
||||
protected function register_handler(moodle1_handler $handler, array $elements) {
|
||||
|
||||
// first iteration, push them to new array, indexed by name
|
||||
// to detect duplicates in names or paths
|
||||
$names = array();
|
||||
$paths = array();
|
||||
foreach($elements as $element) {
|
||||
if (!$element instanceof convert_path) {
|
||||
throw new convert_exception('path_element_wrong_class', get_class($element));
|
||||
}
|
||||
if (array_key_exists($element->get_name(), $names)) {
|
||||
throw new convert_exception('path_element_name_alreadyexists', $element->get_name());
|
||||
}
|
||||
if (array_key_exists($element->get_path(), $paths)) {
|
||||
throw new convert_exception('path_element_path_alreadyexists', $element->get_path());
|
||||
}
|
||||
$names[$element->get_name()] = true;
|
||||
$paths[$element->get_path()] = $element;
|
||||
}
|
||||
|
||||
// now, for each element not having a processing object yet, assign the handler
|
||||
// if the element is not a memeber of a group
|
||||
foreach($paths as $key => $element) {
|
||||
if (is_null($element->get_processing_object()) and !$this->grouped_parent_exists($element, $paths)) {
|
||||
$paths[$key]->set_processing_object($handler);
|
||||
}
|
||||
// add the element path to the processor
|
||||
$this->xmlprocessor->add_path($element->get_path(), $element->is_grouped());
|
||||
}
|
||||
|
||||
// done, store the paths (duplicates by path are discarded)
|
||||
$this->pathelements = array_merge($this->pathelements, $paths);
|
||||
|
||||
// remove the injected plugin name element from the MOD and BLOCK paths
|
||||
// and register such collapsed path, too
|
||||
foreach ($elements as $element) {
|
||||
$path = $element->get_path();
|
||||
$path = preg_replace('/^\/MOODLE_BACKUP\/COURSE\/MODULES\/MOD\/(\w+)\//', '/MOODLE_BACKUP/COURSE/MODULES/MOD/', $path);
|
||||
$path = preg_replace('/^\/MOODLE_BACKUP\/COURSE\/BLOCKS\/BLOCK\/(\w+)\//', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/', $path);
|
||||
if (!empty($path) and $path != $element->get_path()) {
|
||||
$this->xmlprocessor->add_path($path, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by {@link self::register_handler()}
|
||||
*
|
||||
* @param convert_path $pelement path element
|
||||
* @param array of convert_path instances
|
||||
* @return bool true if grouped parent was found, false otherwise
|
||||
*/
|
||||
protected function grouped_parent_exists($pelement, $elements) {
|
||||
|
||||
foreach ($elements as $element) {
|
||||
if ($pelement->get_path() == $element->get_path()) {
|
||||
// don't compare against itself
|
||||
continue;
|
||||
}
|
||||
// if the element is grouped and it is a parent of pelement, return true
|
||||
if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no grouped parent found
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the data obtained from the XML parser processor
|
||||
*
|
||||
* This methods receives one chunk of information from the XML parser
|
||||
* processor and dispatches it, following the naming rules.
|
||||
* We are expanding the modules and blocks paths here to include the plugin's name.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function process_chunk($data) {
|
||||
|
||||
$path = $data['path'];
|
||||
|
||||
// expand the MOD paths so that they contain the module name
|
||||
if ($path === '/MOODLE_BACKUP/COURSE/MODULES/MOD') {
|
||||
$this->currentmod = strtoupper($data['tags']['MODTYPE']);
|
||||
$path = '/MOODLE_BACKUP/COURSE/MODULES/MOD/' . $this->currentmod;
|
||||
|
||||
} else if (strpos($path, '/MOODLE_BACKUP/COURSE/MODULES/MOD') === 0) {
|
||||
$path = str_replace('/MOODLE_BACKUP/COURSE/MODULES/MOD', '/MOODLE_BACKUP/COURSE/MODULES/MOD/' . $this->currentmod, $path);
|
||||
}
|
||||
|
||||
// expand the BLOCK paths so that they contain the module name
|
||||
if ($path === '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') {
|
||||
$this->currentblock = strtoupper($data['tags']['NAME']);
|
||||
$path = '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock;
|
||||
|
||||
} else if (strpos($path, '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') === 0) {
|
||||
$path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentmod, $path);
|
||||
}
|
||||
|
||||
if ($path !== $data['path']) {
|
||||
if (!array_key_exists($path, $this->pathelements)) {
|
||||
// no handler registered for the transformed MOD or BLOCK path
|
||||
// todo add this event to the convert log instead of debugging
|
||||
//debugging('No handler registered for the path ' . $path);
|
||||
return;
|
||||
|
||||
} else {
|
||||
// pretend as if the original $data contained the tranformed path
|
||||
$data['path'] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists($data['path'], $this->pathelements)) {
|
||||
// path added to the processor without the handler
|
||||
throw new convert_exception('missing_path_handler', $data['path']);
|
||||
}
|
||||
|
||||
$element = $this->pathelements[$data['path']];
|
||||
$object = $element->get_processing_object();
|
||||
$method = $element->get_processing_method();
|
||||
$rdata = null; // data returned by the processing method, if any
|
||||
|
||||
if (empty($object)) {
|
||||
throw new convert_exception('missing_processing_object', $object);
|
||||
}
|
||||
|
||||
// release the lock if we aren't anymore within children of it
|
||||
if (!is_null($this->pathlock) and strpos($data['path'], $this->pathlock) === false) {
|
||||
$this->pathlock = null;
|
||||
}
|
||||
|
||||
// if the path is not locked, apply the element's recipes and dispatch
|
||||
// the cooked tags to the processing method
|
||||
if (is_null($this->pathlock)) {
|
||||
$data['tags'] = $element->apply_recipes($data['tags']);
|
||||
$rdata = $object->$method($data['tags']);
|
||||
}
|
||||
|
||||
// if the dispatched method returned SKIP_ALL_CHILDREN, remember the current path
|
||||
// and lock it so that its children are not dispatched
|
||||
if ($rdata === self::SKIP_ALL_CHILDREN) {
|
||||
// check we haven't any previous lock
|
||||
if (!is_null($this->pathlock)) {
|
||||
throw new convert_exception('already_locked_path', $data['path']);
|
||||
}
|
||||
// set the lock - nothing below the current path will be dispatched
|
||||
$this->pathlock = $data['path'] . '/';
|
||||
|
||||
// if the method has returned any info, set element data to it
|
||||
} else if (!is_null($rdata)) {
|
||||
$element->set_data($rdata);
|
||||
|
||||
// use just the cooked parsed data otherwise
|
||||
} else {
|
||||
$element->set_data($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes operations required at the start of a watched path
|
||||
*
|
||||
* Note that this is called before the MOD and BLOCK paths are expanded
|
||||
* so the current plugin is not known yet.
|
||||
*
|
||||
* @todo dispatch the message to the interested handlers
|
||||
* @param string $path in the original file
|
||||
*/
|
||||
public function path_start_reached($path) {
|
||||
print_object("start reached: $path"); // DONOTCOMMIT
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes operations required at the end of a watched path
|
||||
*
|
||||
* @todo dispatch the message to the interested handlers
|
||||
* @param string $path in the original file
|
||||
*/
|
||||
public function path_end_reached($path) {
|
||||
print_object("end reached: $path"); // DONOTCOMMIT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* XML parser processor
|
||||
*/
|
||||
class moodle1_parser_processor extends grouped_parser_processor {
|
||||
|
||||
/** @var moodle1_converter */
|
||||
protected $converter;
|
||||
|
||||
public function __construct(moodle1_converter $converter) {
|
||||
$this->converter = $converter;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide NULL and legacy file.php uses decoding
|
||||
*/
|
||||
public function process_cdata($cdata) {
|
||||
global $CFG;
|
||||
|
||||
if ($cdata === '$@NULL@$') { // Some cases we know we can skip complete processing
|
||||
return null;
|
||||
} else if ($cdata === '') {
|
||||
return '';
|
||||
} else if (is_numeric($cdata)) {
|
||||
return $cdata;
|
||||
} else if (strlen($cdata) < 32) { // Impossible to have one link in 32cc
|
||||
return $cdata; // (http://10.0.0.1/file.php/1/1.jpg, http://10.0.0.1/mod/url/view.php?id=)
|
||||
} else if (strpos($cdata, '$@FILEPHP@$') === false) { // No $@FILEPHP@$, nothing to convert
|
||||
return $cdata;
|
||||
}
|
||||
// Decode file.php calls
|
||||
$search = array ("$@FILEPHP@$");
|
||||
$replace = array(get_file_url($this->courseid));
|
||||
$result = str_replace($search, $replace, $cdata);
|
||||
// Now $@SLASH@$ and $@FORCEDOWNLOAD@$ MDL-18799
|
||||
$search = array('$@SLASH@$', '$@FORCEDOWNLOAD@$');
|
||||
if ($CFG->slasharguments) {
|
||||
$replace = array('/', '?forcedownload=1');
|
||||
} else {
|
||||
$replace = array('%2F', '&forcedownload=1');
|
||||
}
|
||||
return str_replace($search, $replace, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method so we'll be able to skip
|
||||
* dispatching some well-known chunks, like the
|
||||
* ones being 100% part of subplugins stuff. Useful
|
||||
* for allowing development without having all the
|
||||
* possible restore subplugins defined
|
||||
*/
|
||||
protected function postprocess_chunk($data) {
|
||||
|
||||
// Iterate over all the data tags, if any of them is
|
||||
// not 'subplugin_XXXX' or has value, then it's a valid chunk,
|
||||
// pass it to standard (parent) processing of chunks.
|
||||
foreach ($data['tags'] as $key => $value) {
|
||||
if (trim($value) !== '' || strpos($key, 'subplugin_') !== 0) {
|
||||
parent::postprocess_chunk($data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Arrived here, all the tags correspond to sublplugins and are empty,
|
||||
// skip the chunk, and debug_developer notice
|
||||
$this->chunks--; // not counted
|
||||
debugging('Missing support on restore for ' . clean_param($data['path'], PARAM_PATH) .
|
||||
' subplugin (' . implode(', ', array_keys($data['tags'])) .')', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the data chunk to the converter class
|
||||
*
|
||||
* @param array $data the chunk of parsed data
|
||||
*/
|
||||
protected function dispatch_chunk($data) {
|
||||
$this->converter->process_chunk($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the converter at the start of a watched path
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
protected function notify_path_start($path) {
|
||||
$this->converter->path_start_reached($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the converter at the end of a watched path
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
protected function notify_path_end($path) {
|
||||
$this->converter->path_end_reached($path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a path to be converted from XML file
|
||||
*
|
||||
* This was created as a copy of {@link restore_path_element} and should be refactored
|
||||
* probably.
|
||||
*/
|
||||
class convert_path {
|
||||
|
||||
/** @var string name of the element */
|
||||
protected $name;
|
||||
|
||||
/** @var string path within the XML file this element will handle */
|
||||
protected $path;
|
||||
|
||||
/** @var bool flag to define if this element will get child ones grouped or no */
|
||||
protected $grouped;
|
||||
|
||||
/** @var object object instance in charge of processing this element. */
|
||||
protected $pobject = null;
|
||||
|
||||
/** @var string the name of the processing method */
|
||||
protected $pmethod = null;
|
||||
|
||||
/** @var mixed last data read for this element or returned data by processing method */
|
||||
protected $data = null;
|
||||
|
||||
/** @var array of deprecated fields that are skipped and not converted */
|
||||
protected $skipfields = array();
|
||||
|
||||
/** @var array of fields renaming */
|
||||
protected $renamefields = array();
|
||||
|
||||
/** @var array of new fields to add and their initial values */
|
||||
protected $newfields = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name name of the element
|
||||
* @param string $path path of the element
|
||||
* @param array $recipe basic description of the structure conversion
|
||||
* @param bool $grouped to gather information in grouped mode or no
|
||||
*/
|
||||
public function __construct($name, $path, array $recipe = array(), $grouped = false) {
|
||||
|
||||
$this->validate_name($name);
|
||||
|
||||
$this->name = $name;
|
||||
$this->path = $path;
|
||||
$this->grouped = $grouped;
|
||||
|
||||
// set the default processing method name
|
||||
$this->set_processing_method('process_' . $name);
|
||||
|
||||
if (isset($recipe['skipfields']) and is_array($recipe['skipfields'])) {
|
||||
$this->set_skipped_fields($recipe['skipfields']);
|
||||
}
|
||||
if (isset($recipe['renamefields']) and is_array($recipe['renamefields'])) {
|
||||
$this->set_renamed_fields($recipe['renamefields']);
|
||||
}
|
||||
if (isset($recipe['newfields']) and is_array($recipe['newfields'])) {
|
||||
$this->set_new_fields($recipe['newfields']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sets the given processing object
|
||||
*
|
||||
* @param object $pobject processing object, must provide a method to be called
|
||||
*/
|
||||
public function set_processing_object($pobject) {
|
||||
$this->validate_pobject($pobject);
|
||||
$this->pobject = $pobject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the processing method
|
||||
*
|
||||
* @param string $pmethod
|
||||
*/
|
||||
public function set_processing_method($pmethod) {
|
||||
$this->pmethod = $pmethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element data
|
||||
*
|
||||
* @param mixed
|
||||
*/
|
||||
public function set_data($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of deprecated fields to skip
|
||||
*
|
||||
* @param array $fields
|
||||
*/
|
||||
public function set_skipped_fields(array $fields) {
|
||||
$this->skipfields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the required new names of the current fields
|
||||
*
|
||||
* @param array $fields (string)$currentname => (string)$newname
|
||||
*/
|
||||
public function set_renamed_fields(array $fields) {
|
||||
$this->renamefields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new fields and their values
|
||||
*
|
||||
* @param array $fields (string)$field => (mixed)value
|
||||
*/
|
||||
public function set_new_fields(array $fields) {
|
||||
$this->newfields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cooks the parsed tags data by applying known recipes
|
||||
*
|
||||
* Recipes are used for common trivial operations like adding new fields
|
||||
* or renaming fields. The handler's processing method receives cooked
|
||||
* data.
|
||||
*
|
||||
* @param array $data the contents of the element
|
||||
* @return array
|
||||
*/
|
||||
public function apply_recipes(array $data) {
|
||||
|
||||
$cooked = array();
|
||||
|
||||
foreach ($data as $name => $value) {
|
||||
// lower case rocks!
|
||||
$name = strtolower($name);
|
||||
|
||||
// fields renaming
|
||||
if (array_key_exists($name, $this->renamefields)) {
|
||||
$name = $this->renamefields[$name];
|
||||
}
|
||||
|
||||
$cooked[$name] = $value;
|
||||
}
|
||||
|
||||
// adding new fields
|
||||
foreach ($this->newfields as $name => $value) {
|
||||
$cooked[$name] = $value;
|
||||
}
|
||||
|
||||
return $cooked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the element given name
|
||||
*/
|
||||
public function get_name() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the path to the element
|
||||
*/
|
||||
public function get_path() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool flag to define if this element will get child ones grouped or no
|
||||
*/
|
||||
public function is_grouped() {
|
||||
return $this->grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return object the processing object providing the processing method
|
||||
*/
|
||||
public function get_processing_object() {
|
||||
return $this->pobject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the name of the method to call to process the element
|
||||
*/
|
||||
public function get_processing_method() {
|
||||
return $this->pmethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed the element data
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
|
||||
/// end of public API //////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Makes sure the given name is a valid element name
|
||||
*
|
||||
* Note it may look as if we used exceptions for code flow control here. That's not the case
|
||||
* as we actually validate the code, not the user data. And the code is supposed to be
|
||||
* correct.
|
||||
*
|
||||
* @param string @name the element given name
|
||||
* @throws convert_path_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_name($name) {
|
||||
// Validate various name constraints, throwing exception if needed
|
||||
if (empty($name)) {
|
||||
throw new convert_path_exception('convert_path_emptyname', $name);
|
||||
}
|
||||
if (preg_replace('/\s/', '', $name) != $name) {
|
||||
throw new convert_path_exception('convert_path_whitespace', $name);
|
||||
}
|
||||
if (preg_replace('/[^\x30-\x39\x41-\x5a\x5f\x61-\x7a]/', '', $name) != $name) {
|
||||
throw new convert_path_exception('convert_path_notasciiname', $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the given object is a valid processing object
|
||||
*
|
||||
* The processing object must be an object providing the element's processing method.
|
||||
* Note it may look as if we used exceptions for code flow control here. That's not the case
|
||||
* as we actually validate the code, not the user data. And the code is supposed to be
|
||||
* correct.
|
||||
*
|
||||
* @param object $pobject
|
||||
* @throws convert_path_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_pobject($pobject) {
|
||||
if (!is_object($pobject)) {
|
||||
throw new convert_path_exception('convert_path_no_object', $pobject);
|
||||
}
|
||||
if (!method_exists($pobject, $this->get_processing_method())) {
|
||||
throw new convert_path_exception('convert_path_missingmethod', $this->get_processing_method());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exception being thrown by {@link convert_path} methods
|
||||
*/
|
||||
class convert_path_exception extends moodle_exception {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode key for the corresponding error string
|
||||
* @param mixed $a extra words and phrases that might be required by the error string
|
||||
* @param string $debuginfo optional debugging information
|
||||
*/
|
||||
public function __construct($errorcode, $a = null, $debuginfo = null) {
|
||||
parent::__construct($errorcode, '', '', $a, $debuginfo);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -26,7 +26,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/converter/moodle1/converter.class.php');
|
||||
require_once($CFG->dirroot . '/backup/converter/moodle1/lib.php');
|
||||
|
||||
class moodle1_converter_test extends UnitTestCase {
|
||||
|
||||
@@ -58,7 +58,7 @@ class moodle1_converter_test extends UnitTestCase {
|
||||
$this->assertEqual(backup::FORMAT_MOODLE1, $detected);
|
||||
}
|
||||
|
||||
public function test_convert() {
|
||||
public function test_convert_factory() {
|
||||
$converter = convert_factory::converter('moodle1', $this->tempdir);
|
||||
$this->assertIsA($converter, 'moodle1_converter');
|
||||
$converter->convert();
|
||||
@@ -50,7 +50,7 @@ abstract class moodle1_structure_step extends convert_structure_step {
|
||||
*/
|
||||
public function open_xml_writer() {
|
||||
if (!$this->xmlwriter instanceof xml_writer) {
|
||||
$fullpath = $this->get_basepath().'/'.$this->get_xml_filename();
|
||||
$fullpath = $this->get_basepath() . '/' . $this->get_xml_filename();
|
||||
$directory = pathinfo($fullpath, PATHINFO_DIRNAME);
|
||||
|
||||
if (!check_dir_exists($directory)) {
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @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');
|
||||
|
||||
class moodle1_root_task extends convert_task {
|
||||
/**
|
||||
* Function responsible for building the steps of any task
|
||||
* (must set the $built property to true)
|
||||
*/
|
||||
public function build() {
|
||||
$this->add_step(new convert_create_and_clean_temp_stuff('create_and_clean_temp_stuff'));
|
||||
|
||||
// At the end, mark it as built
|
||||
$this->built = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Not used at the moment
|
||||
*/
|
||||
class moodle1_final_task extends convert_task {
|
||||
/**
|
||||
* Function responsible for building the steps of any task
|
||||
* (must set the $built property to true)
|
||||
*/
|
||||
public function build() {
|
||||
$this->add_step(new convert_drop_and_clean_temp_stuff('drop_and_clean_temp_stuff'));
|
||||
|
||||
// At the end, mark it as built
|
||||
$this->built = true;
|
||||
}
|
||||
}
|
||||
|
||||
class moodle1_course_task extends convert_task {
|
||||
/**
|
||||
* Create all the steps that will be part of this task
|
||||
*/
|
||||
public function build() {
|
||||
|
||||
$this->add_step(new moodle1_course_structure_step('course'));
|
||||
$this->add_step(new moodle1_section_structure_step('course_section'));
|
||||
$this->add_step(new moodle1_block_structure_step('course_blocks'));
|
||||
$this->add_step(new moodle1_info_structure_step('info'));
|
||||
|
||||
// At the end, mark it as built
|
||||
$this->built = true;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class moodle1_plugin_task extends convert_task {
|
||||
/**
|
||||
* Plugin specific steps
|
||||
*/
|
||||
abstract protected function define_my_steps();
|
||||
}
|
||||
|
||||
abstract class moodle1_activity_task extends moodle1_plugin_task {
|
||||
/**
|
||||
* Function responsible for building the steps of any task
|
||||
* (must set the $built property to true)
|
||||
*/
|
||||
public function build() {
|
||||
$this->define_my_steps();
|
||||
|
||||
// @todo Risky?
|
||||
list($plugin, $name) = explode('_', $this->name);
|
||||
|
||||
$this->add_step(new moodle1_module_structure_step("{$this->name}_module", $name));
|
||||
$this->built = true;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class moodle1_block_task extends moodle1_plugin_task {
|
||||
/**
|
||||
* Function responsible for building the steps of any task
|
||||
* (must set the $built property to true)
|
||||
*/
|
||||
public function build() {
|
||||
$this->define_my_steps();
|
||||
$this->built = true;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Do convert plan related set up
|
||||
*/
|
||||
class convert_create_and_clean_temp_stuff extends convert_execution_step {
|
||||
|
||||
protected function define_execution() {
|
||||
backup_controller_dbops::create_backup_ids_temp_table($this->get_convertid()); // Create ids temp table
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do convert plan related tear down
|
||||
*/
|
||||
class convert_drop_and_clean_temp_stuff extends convert_execution_step {
|
||||
|
||||
protected function define_execution() {
|
||||
// We want to run after execution
|
||||
}
|
||||
|
||||
public function execute_after_convert() {
|
||||
backup_controller_dbops::drop_backup_ids_temp_table($this->get_convertid()); // Drop ids temp table
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Base class for all converters using plan/tasks/steps pattern
|
||||
*
|
||||
* All converters that use {@link convert_plan} must extend this class.
|
||||
*/
|
||||
abstract class planned_converter extends base_converter {
|
||||
|
||||
/** @var convert_plan */
|
||||
protected $plan;
|
||||
/** @var progressive_parser */
|
||||
protected $xmlparser;
|
||||
/** @var convert_structure_parser_processor */
|
||||
protected $xmlprocessor;
|
||||
/** @var array path elements to process */
|
||||
protected $pathelements = array();
|
||||
/** @todo needed? redo? path currently locking processing of children */
|
||||
protected $pathlock;
|
||||
|
||||
/**
|
||||
* Instructs the dispatcher to ignore all children below path processor returning it
|
||||
*/
|
||||
const SKIP_ALL_CHILDREN = -991399;
|
||||
|
||||
/**
|
||||
* Return the plan instance, instatinate it if it does not exist yet
|
||||
*
|
||||
* @return convert_plan
|
||||
*/
|
||||
public function get_plan() {
|
||||
if (!$this->plan instanceof convert_plan) {
|
||||
$this->plan = new convert_plan($this);
|
||||
}
|
||||
return $this->plan;
|
||||
}
|
||||
|
||||
abstract public function build_plan();
|
||||
|
||||
public function execute() {
|
||||
$this->get_plan()->build(); // Ends up calling $this->build_plan()
|
||||
$this->get_plan()->execute();
|
||||
$this->xmlparser->process(); // @todo When to really do this?
|
||||
$this->get_plan()->execute_after_convert();
|
||||
}
|
||||
|
||||
public function destroy() {
|
||||
parent::destroy();
|
||||
$this->get_plan()->destroy();
|
||||
}
|
||||
|
||||
// @todo Validation here is weak, probably should validate against a data members that keep track of all names/paths
|
||||
public function add_structures($processingobject, array $structures) {
|
||||
// First iteration, push them to new array, indexed by name
|
||||
// detecting duplicates in names or paths
|
||||
$names = array();
|
||||
$paths = array();
|
||||
foreach($structures as $element) {
|
||||
if (!$element instanceof convert_path_element) {
|
||||
throw new restore_step_exception('restore_path_element_wrong_class', get_class($element)); // @todo Change exception
|
||||
}
|
||||
if (array_key_exists($element->get_name(), $names)) {
|
||||
throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name()); // @todo Change exception
|
||||
}
|
||||
if (array_key_exists($element->get_path(), $paths)) {
|
||||
throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path()); // @todo Change exception
|
||||
}
|
||||
$names[$element->get_name()] = true;
|
||||
$paths[$element->get_path()] = $element;
|
||||
}
|
||||
// Now, for each element not having one processing object, if
|
||||
// not child of grouped element, assign $this (the step itself) as processing element
|
||||
// Note method must exist or we'll get one @restore_path_element_exception
|
||||
foreach($paths as $key => $element) {
|
||||
if ($element->get_processing_object() === null && !$this->grouped_parent_exists($element, $paths)) {
|
||||
$paths[$key]->set_processing_object($processingobject);
|
||||
}
|
||||
// Add element path to the processor
|
||||
$this->xmlprocessor->add_path($element->get_path(), $element->is_grouped());
|
||||
}
|
||||
// Done, add them to pathelements (dupes by key - path - are discarded)
|
||||
$this->pathelements = array_merge($this->pathelements, $paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one pathelement, return true if grouped parent was found
|
||||
*/
|
||||
protected function grouped_parent_exists($pelement, $elements) {
|
||||
foreach ($elements as $element) {
|
||||
if ($pelement->get_path() == $element->get_path()) {
|
||||
continue; // Don't compare against itself
|
||||
}
|
||||
// If element is grouped and parent of pelement, return true
|
||||
if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // no grouped parent found
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive one chunk of information form the xml parser processor and
|
||||
* dispatch it, following the naming rules
|
||||
*/
|
||||
public function process($data) {
|
||||
if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
|
||||
throw new restore_step_exception('restore_structure_step_missing_path', $data['path']); // @todo Change exception
|
||||
}
|
||||
$element = $this->pathelements[$data['path']];
|
||||
$object = $element->get_processing_object();
|
||||
$method = $element->get_processing_method();
|
||||
$rdata = null;
|
||||
if (empty($object)) { // No processing object defined
|
||||
throw new restore_step_exception('restore_structure_step_missing_pobject', $object); // @todo Change exception
|
||||
}
|
||||
// Release the lock if we aren't anymore within children of it
|
||||
if (!is_null($this->pathlock) and strpos($data['path'], $this->pathlock) === false) {
|
||||
$this->pathlock = null;
|
||||
}
|
||||
if (is_null($this->pathlock)) { // Only dispatch if there isn't any lock
|
||||
$rdata = $object->$method($data['tags']); // Dispatch to proper object/method
|
||||
}
|
||||
|
||||
// If the dispatched method returns SKIP_ALL_CHILDREN, we grab current path in order to
|
||||
// lock dispatching to any children
|
||||
if ($rdata === self::SKIP_ALL_CHILDREN) {
|
||||
// Check we haven't any previous lock
|
||||
if (!is_null($this->pathlock)) {
|
||||
throw new restore_step_exception('restore_structure_step_already_skipping', $data['path']); // @todo Change exception
|
||||
}
|
||||
// Set the lock
|
||||
$this->pathlock = $data['path'] . '/'; // Lock everything below current path
|
||||
|
||||
// Continue with normal processing of return values
|
||||
} else if ($rdata !== null) { // If the method has returned any info, set element data to it
|
||||
$element->set_data($rdata);
|
||||
} else { // Else, put the original parsed data
|
||||
$element->set_data($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,14 @@ abstract class convert_factory {
|
||||
* @throws coding_exception
|
||||
* @param $name The converter name
|
||||
* @param $tempdir The temp directory to operate on
|
||||
* @return base_converter|plan_converter
|
||||
* @return base_converter
|
||||
*/
|
||||
public static function converter($name, $tempdir) {
|
||||
global $CFG;
|
||||
|
||||
$name = clean_param($name, PARAM_SAFEDIR);
|
||||
|
||||
$classfile = "$CFG->dirroot/backup/converter/$name/converter.class.php";
|
||||
$classfile = "$CFG->dirroot/backup/converter/$name/lib.php";
|
||||
$classname = "{$name}_converter";
|
||||
|
||||
if (!file_exists($classfile)) {
|
||||
@@ -55,52 +55,4 @@ abstract class convert_factory {
|
||||
}
|
||||
return new $classname($tempdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs through all plugins of a specific type and instantiates their task class
|
||||
*
|
||||
* @throws coding_exception
|
||||
* @param string $type The plugin type
|
||||
* @param string $format The convert format
|
||||
* @param string $extra Extra naming structure
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugin_tasks($type, $format, $extra = NULL) {
|
||||
global $CFG; // REQUIRED by task file includes
|
||||
|
||||
if (is_null($extra)) {
|
||||
$extra = $type;
|
||||
}
|
||||
$tasks = array();
|
||||
$plugins = get_plugin_list($type);
|
||||
foreach ($plugins as $name => $dir) {
|
||||
$taskfile = "$dir/backup/$format/convert_{$name}_{$extra}_task.class.php";
|
||||
$taskclass = "{$format}_{$name}_{$extra}_task";
|
||||
if (!file_exists($taskfile)) {
|
||||
continue;
|
||||
}
|
||||
require_once($taskfile);
|
||||
|
||||
if (!class_exists($taskclass)) {
|
||||
throw new coding_exception("The class name should be $taskclass in $taskfile");
|
||||
}
|
||||
$tasks[] = new $taskclass("{$type}_$name");
|
||||
}
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all of the plugin tasks to the given converter's plan
|
||||
*
|
||||
* @param plan_converter $converter The converter to add the plugin tasks to
|
||||
* @param string $type The plugin type
|
||||
* @param string $extra Extra naming structure
|
||||
* @return void
|
||||
*/
|
||||
public static function build_plugin_tasks(plan_converter $converter, $type, $extra = NULL) {
|
||||
$tasks = self::get_plugin_tasks($type, $converter->get_name(), $extra);
|
||||
foreach ($tasks as $task) {
|
||||
$converter->get_plan()->add_task($task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,8 @@ abstract class backup_general_helper extends backup_helper {
|
||||
*/
|
||||
public static function detect_backup_format($tempdir) {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/factories/convert_factory.class.php');
|
||||
|
||||
if (convert_helper::detect_moodle2_format($tempdir)) {
|
||||
return backup::FORMAT_MOODLE;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Provides {@link convert_helper} class
|
||||
* Provides {@link convert_helper} and {@link convert_helper_exception} classes
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||
|
||||
/**
|
||||
* Provides various functionality via its static methods
|
||||
*/
|
||||
@@ -54,16 +56,16 @@ abstract class convert_helper {
|
||||
$converters = array();
|
||||
$plugins = get_list_of_plugins('backup/converter');
|
||||
foreach ($plugins as $name) {
|
||||
$classfile = "$CFG->dirroot/backup/converter/$name/converter.class.php";
|
||||
$classfile = "$CFG->dirroot/backup/converter/$name/lib.php";
|
||||
$classname = "{$name}_converter";
|
||||
|
||||
if (!file_exists($classfile)) {
|
||||
throw new coding_exception("Converter factory error: class file not found $classfile");
|
||||
throw new convert_helper_exception('converter_classfile_not_found', $classfile);
|
||||
}
|
||||
require_once($classfile);
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
throw new coding_exception("Converter factory error: class not found $classname");
|
||||
throw new convert_helper_exception('converter_classname_not_found', $classname);
|
||||
}
|
||||
|
||||
if (call_user_func($classname .'::is_available')) {
|
||||
@@ -87,7 +89,7 @@ abstract class convert_helper {
|
||||
$filepath = $dirpath . '/moodle_backup.xml';
|
||||
|
||||
if (!is_dir($dirpath)) {
|
||||
throw new backup_helper_exception('tmp_backup_directory_not_found', $dirpath);
|
||||
throw new converter_helper_exception('tmp_backup_directory_not_found', $dirpath);
|
||||
}
|
||||
|
||||
if (!file_exists($filepath)) {
|
||||
@@ -110,10 +112,10 @@ abstract class convert_helper {
|
||||
/**
|
||||
* Converts the given directory with the backup into moodle2 format
|
||||
*
|
||||
* @throws coding_exception|restore_controller_exception
|
||||
* @param string $tempdir The directory to convert
|
||||
* @param string $format The current format, if already detected
|
||||
* @return void
|
||||
* @throws convert_helper_exception
|
||||
* @return bool false if unable to find the conversion path, true otherwise
|
||||
*/
|
||||
public static function to_moodle2_format($tempdir, $format = null) {
|
||||
|
||||
@@ -127,8 +129,7 @@ abstract class convert_helper {
|
||||
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");
|
||||
throw new convert_helper_exception('class_not_loaded', $classname);
|
||||
}
|
||||
$descriptions[$name] = call_user_func($classname .'::description');
|
||||
}
|
||||
@@ -138,8 +139,7 @@ abstract class convert_helper {
|
||||
|
||||
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');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($path as $name) {
|
||||
@@ -149,8 +149,10 @@ abstract class convert_helper {
|
||||
|
||||
// make sure we ended with moodle2 format
|
||||
if (!self::detect_moodle2_format($tempdir)) {
|
||||
throw new coding_exception('Conversion failed');
|
||||
throw new convert_helper_exception('conversion_failed');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,8 +179,6 @@ abstract class convert_helper {
|
||||
/**
|
||||
* Generate an artificial context ID
|
||||
*
|
||||
* @static
|
||||
* @throws Exception
|
||||
* @param int $instance The moodle component instance ID, same value used for get_context_instance()
|
||||
* @param string $component The moodle component, like block_html, mod_quiz, etc
|
||||
* @param string $converterid The converter ID
|
||||
@@ -191,15 +191,14 @@ abstract class convert_helper {
|
||||
|
||||
// Attempt to retrieve the contextid
|
||||
$contextid = $DB->get_field_select('backup_ids_temp', 'id',
|
||||
$DB->sql_compare_text('info', 100).' = ? AND itemid = ? AND itemname = ?',
|
||||
array($component, $instance, 'context')
|
||||
);
|
||||
$DB->sql_compare_text('info', 100).' = '.$DB->sql_compare_text('?', 100).' AND itemid = ? AND itemname = ?',
|
||||
array($component, $instance, 'context'));
|
||||
|
||||
if (!empty($contextid)) {
|
||||
return $contextid;
|
||||
}
|
||||
|
||||
$context = new stdClass;
|
||||
$context = new stdClass();
|
||||
$context->itemid = $instance;
|
||||
$context->itemname = 'context';
|
||||
$context->info = $component;
|
||||
@@ -211,7 +210,7 @@ abstract class convert_helper {
|
||||
return $id;
|
||||
} else {
|
||||
$msg = self::obj_to_readable($context);
|
||||
throw new Exception(sprintf("Could not insert context record into temp table: %s", $msg));
|
||||
throw new convert_helper_exception('failed_insert_record', $msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +228,7 @@ abstract class convert_helper {
|
||||
* the oriented graph.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Dijkstra's_algorithm
|
||||
* @author David Mudrak <[email protected]>
|
||||
* @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)
|
||||
@@ -246,7 +246,7 @@ abstract class convert_helper {
|
||||
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);
|
||||
throw new convert_helper_exception('invalid_converter_description', $converter);
|
||||
}
|
||||
|
||||
if (!isset($paths[$from][$to])) {
|
||||
@@ -354,3 +354,22 @@ abstract class convert_helper {
|
||||
return $conversionpath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General convert_helper related exception
|
||||
*
|
||||
* @author David Mudrak <[email protected]>
|
||||
*/
|
||||
class convert_helper_exception extends moodle_exception {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $errorcode key for the corresponding error string
|
||||
* @param object $a extra words and phrases that might be required in the error string
|
||||
* @param string $debuginfo optional debugging information
|
||||
*/
|
||||
public function __construct($errorcode, $a = null, $debuginfo = null) {
|
||||
parent::__construct($errorcode, '', '', $a, $debuginfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class convert_structure_parser_processor extends grouped_parser_processor {
|
||||
/**
|
||||
* @var plan_converter
|
||||
*/
|
||||
protected $converter;
|
||||
|
||||
public function __construct(plan_converter $converter) {
|
||||
$this->converter = $converter;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide NULL and legacy file.php uses decoding
|
||||
*/
|
||||
public function process_cdata($cdata) {
|
||||
global $CFG;
|
||||
if ($cdata === '$@NULL@$') { // Some cases we know we can skip complete processing
|
||||
return null;
|
||||
} else if ($cdata === '') {
|
||||
return '';
|
||||
} else if (is_numeric($cdata)) {
|
||||
return $cdata;
|
||||
} else if (strlen($cdata) < 32) { // Impossible to have one link in 32cc
|
||||
return $cdata; // (http://10.0.0.1/file.php/1/1.jpg, http://10.0.0.1/mod/url/view.php?id=)
|
||||
} else if (strpos($cdata, '$@FILEPHP@$') === false) { // No $@FILEPHP@$, nothing to convert
|
||||
return $cdata;
|
||||
}
|
||||
// Decode file.php calls
|
||||
$search = array ("$@FILEPHP@$");
|
||||
$replace = array(get_file_url($this->courseid));
|
||||
$result = str_replace($search, $replace, $cdata);
|
||||
// Now $@SLASH@$ and $@FORCEDOWNLOAD@$ MDL-18799
|
||||
$search = array('$@SLASH@$', '$@FORCEDOWNLOAD@$');
|
||||
if ($CFG->slasharguments) {
|
||||
$replace = array('/', '?forcedownload=1');
|
||||
} else {
|
||||
$replace = array('%2F', '&forcedownload=1');
|
||||
}
|
||||
return str_replace($search, $replace, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method so we'll be able to skip
|
||||
* dispatching some well-known chunks, like the
|
||||
* ones being 100% part of subplugins stuff. Useful
|
||||
* for allowing development without having all the
|
||||
* possible restore subplugins defined
|
||||
*/
|
||||
protected function postprocess_chunk($data) {
|
||||
|
||||
// Iterate over all the data tags, if any of them is
|
||||
// not 'subplugin_XXXX' or has value, then it's a valid chunk,
|
||||
// pass it to standard (parent) processing of chunks.
|
||||
foreach ($data['tags'] as $key => $value) {
|
||||
if (trim($value) !== '' || strpos($key, 'subplugin_') !== 0) {
|
||||
parent::postprocess_chunk($data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Arrived here, all the tags correspond to sublplugins and are empty,
|
||||
// skip the chunk, and debug_developer notice
|
||||
$this->chunks--; // not counted
|
||||
debugging('Missing support on restore for ' . clean_param($data['path'], PARAM_PATH) .
|
||||
' subplugin (' . implode(', ', array_keys($data['tags'])) .')', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
protected function dispatch_chunk($data) {
|
||||
$this->converter->process($data);
|
||||
}
|
||||
|
||||
protected function notify_path_start($path) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
protected function notify_path_end($path) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
|
||||
|
||||
/**
|
||||
* Provides access to the protected methods we need to test
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Makes sure that all general code needed by backup-convert code is included
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
@@ -24,35 +26,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include all the convert stuff needed
|
||||
require_once($CFG->dirroot.'/backup/util/interfaces/checksumable.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/interfaces/executable.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/interfaces/loggable.class.php');
|
||||
require_once($CFG->dirroot.'/backup/backup.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/xml/xml_writer.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/xml/output/xml_output.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/xml/output/file_xml_output.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/dbops/backup_dbops.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/dbops/backup_controller_dbops.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/factories/convert_factory.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/converter/base_converter.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/converter/plan_converter.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/helper/convert_helper.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/base_plan.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/base_step.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/base_task.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_plan.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_step.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_task.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_structure_step.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_execution_step.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/structure/restore_path_element.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/structure/convert_path_element.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_execution_step.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/helper/convert_structure_parser_processor.class.php');
|
||||
require_once($CFG->dirroot.'/backup/moodle2/convert_stepslib.php');
|
||||
require_once($CFG->dirroot.'/backup/util/xml/parser/progressive_parser.class.php');
|
||||
|
||||
// And some moodle stuff too
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php'); // req by backup.class.php
|
||||
require_once($CFG->dirroot . '/backup/backup.class.php'); // provides backup::FORMAT_xxx constants
|
||||
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
|
||||
require_once($CFG->dirroot . '/backup/util/factories/convert_factory.class.php');
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage backup-convert
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class convert_path_element extends restore_path_element {
|
||||
public function get_processing_method() {
|
||||
return 'convert_' . $this->get_name();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Provides support for the conversion of moodle1 backup to the moodle2 format
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage forum
|
||||
* @copyright 2011 Mark Nielsen <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/mod/forum/backup/moodle1/convert_forum_stepslib.php');
|
||||
|
||||
/**
|
||||
* Convert from Moodle 1 forum task
|
||||
*/
|
||||
class moodle1_forum_activity_task extends moodle1_activity_task {
|
||||
/**
|
||||
* Function responsible for building the steps of any task
|
||||
* (must set the $built property to true)
|
||||
*/
|
||||
public function define_my_steps() {
|
||||
$this->add_step(new moodle1_forum_activity_structure_step('forum'));
|
||||
}
|
||||
}
|
||||
+20
-11
@@ -27,29 +27,38 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Convert forum
|
||||
* Forum conversion handler
|
||||
*/
|
||||
class moodle1_forum_activity_structure_step extends convert_structure_step {
|
||||
class moodle1_mod_forum_handler extends moodle1_mod_handler {
|
||||
|
||||
/**
|
||||
* Function that will return the structure to be processed by this convert_step.
|
||||
* Must return one array of @convert_path_element elements
|
||||
* Declare the paths in moodle.xml we are able to convert
|
||||
*
|
||||
* NOTE: /MOD/ACTIVITYNAME XML path does not actually exist. The moodle1_converter
|
||||
* class automatically transforms the /MOD path to include the activity name.
|
||||
* The method returns list of {@link convert_path} instances.
|
||||
* For each path returned, the corresponding conversion method must be
|
||||
* defined.
|
||||
*
|
||||
* Note that the paths /MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM do not
|
||||
* actually exist in the file. The last element with the module name was
|
||||
* appended by the moodle1_converter class.
|
||||
*
|
||||
* @return array of {@link convert_path} instances
|
||||
*/
|
||||
protected function define_structure() {
|
||||
public function get_paths() {
|
||||
return array(
|
||||
new convert_path_element('forum', '/MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM'),
|
||||
// new convert_path_element('foo', '/MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM/FOO'), // Example of sub-path
|
||||
new convert_path('forum', '/MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM'),
|
||||
// new convert_path('foo', '/MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM/FOO'), // Example of sub-path
|
||||
);
|
||||
}
|
||||
|
||||
public function convert_forum($data) {
|
||||
/**
|
||||
* Converts /MOODLE_BACKUP/COURSE/MODULES/MOD/FORUM data
|
||||
*/
|
||||
public function process_forum($data) {
|
||||
print_object($data);
|
||||
}
|
||||
|
||||
public function convert_foo($data) {
|
||||
public function process_foo($data) {
|
||||
print_object($data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user