mirror of
https://github.com/moodle/moodle.git
synced 2025-02-08 09:02:07 +01:00
Attempting to write up processing of the moodle.xml file - the converter collects all of the element paths to watch and then dispatches them to the convert_structure_step classes.
This commit is contained in:
parent
c5c8b3503a
commit
8450e2c41f
@ -25,4 +25,13 @@ abstract class moodle1_converter extends plan_converter {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function build_plan() {
|
||||
$this->xmlparser = new progressive_parser();
|
||||
$this->xmlparser->set_file($this->get_tempdir() . '/moodle.xml');
|
||||
$this->xmlprocessor = new convert_structure_parser_processor($this); // @todo Probably move this
|
||||
$this->xmlparser->set_processor($this->xmlprocessor);
|
||||
|
||||
|
||||
$xmlparser->process(); // @todo When to really do this?
|
||||
}
|
||||
}
|
@ -4,8 +4,29 @@
|
||||
*/
|
||||
abstract class plan_converter extends base_converter {
|
||||
|
||||
/**
|
||||
* @var convert_plan
|
||||
*/
|
||||
protected $plan;
|
||||
|
||||
/**
|
||||
* @var progressive_parser
|
||||
*/
|
||||
protected $xmlparser;
|
||||
|
||||
/**
|
||||
* @var convert_structure_parser_processor
|
||||
*/
|
||||
protected $xmlprocessor;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $pathelements; // Array of pathelements to process
|
||||
|
||||
// @todo needed? redo?
|
||||
protected $pathlock; // Path currently locking processing of children
|
||||
|
||||
/**
|
||||
* @return convert_plan
|
||||
*/
|
||||
@ -27,4 +48,105 @@ abstract class plan_converter extends base_converter {
|
||||
parent::destroy();
|
||||
$this->get_plan()->destroy();
|
||||
}
|
||||
|
||||
public function add_structures($processingobject, array $structures) {
|
||||
// Override if using class convert_structure_step
|
||||
$this->prepare_pathelements($processingobject, $structures);
|
||||
|
||||
// Add pathelements to processor
|
||||
foreach ($this->pathelements as $element) {
|
||||
$this->xmlprocessor->add_path($element->get_path(), $element->is_grouped());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the pathelements for processing, looking for duplicates, applying
|
||||
* processing objects and other adjustments
|
||||
*/
|
||||
protected function prepare_pathelements($processingobject, $elementsarr) {
|
||||
// First iteration, push them to new array, indexed by name
|
||||
// detecting duplicates in names or paths
|
||||
$names = array();
|
||||
$paths = array();
|
||||
foreach($elementsarr 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 => $pelement) {
|
||||
if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $paths)) {
|
||||
$paths[$key]->set_processing_object($processingobject);
|
||||
}
|
||||
}
|
||||
// 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
|
||||
*/
|
||||
final 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -19,10 +19,13 @@ 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/plan/convert_.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_.class.php');
|
||||
require_once($CFG->dirroot.'/backup/util/plan/convert_.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.'/fileslib.php');
|
@ -26,6 +26,13 @@ class convert_plan extends base_plan implements loggable {
|
||||
return $this->converter->get_convertdir();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plan_converter
|
||||
*/
|
||||
public function get_converter() {
|
||||
return $this->converter;
|
||||
}
|
||||
|
||||
public function get_converterid() {
|
||||
return $this->converter->get_id();
|
||||
}
|
||||
|
@ -16,4 +16,15 @@ abstract class convert_step extends base_step {
|
||||
}
|
||||
return $this->task->get_convertid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws backup_exception
|
||||
* @return plan_converter
|
||||
*/
|
||||
protected function get_converter() {
|
||||
if (!$this->task instanceof convert_task) {
|
||||
throw new backup_exception('not_specified_convert_task'); // @todo Define string
|
||||
}
|
||||
return $this->task->get_converter();
|
||||
}
|
||||
}
|
@ -1,79 +1,23 @@
|
||||
<?php
|
||||
|
||||
// @todo This is copied from backup_structure_step - not tested/modified for convert yet
|
||||
abstract class convert_structure_step extends convert_step {
|
||||
|
||||
protected $filename; // Name of the file to be generated
|
||||
protected $contenttransformer; // xml content transformer being used
|
||||
// (need it here, apart from xml_writer,
|
||||
// thanks to serialized data to process -
|
||||
// say thanks to blocks!)
|
||||
|
||||
/**
|
||||
* Constructor - instantiates one object of this class
|
||||
*/
|
||||
public function __construct($name, $filename, convert_task $task = null) {
|
||||
$this->filename = $filename;
|
||||
$this->contenttransformer = null;
|
||||
parent::__construct($name, $task);
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
final public function execute() {
|
||||
|
||||
if (!$this->execute_condition()) { // Check any condition to execute this
|
||||
return;
|
||||
}
|
||||
|
||||
$fullpath = $this->task->get_taskbasepath();
|
||||
|
||||
// We MUST have one fullpath here, else, error
|
||||
if (empty($fullpath)) {
|
||||
throw new backup_step_exception('backup_structure_step_undefined_fullpath');
|
||||
}
|
||||
|
||||
// Append the filename to the fullpath
|
||||
$fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
|
||||
|
||||
// Create output, transformer, writer, processor
|
||||
$xo = new file_xml_output($fullpath);
|
||||
$xt = null;
|
||||
if (class_exists('backup_xml_transformer')) {
|
||||
$xt = new backup_xml_transformer($this->get_courseid());
|
||||
$this->contenttransformer = $xt; // Save the reference to the transformer
|
||||
// as far as we are going to need it out
|
||||
// from xml_writer (blame serialized data!)
|
||||
}
|
||||
$xw = new xml_writer($xo, $xt);
|
||||
$pr = new backup_structure_processor($xw);
|
||||
|
||||
// Set processor variables from settings
|
||||
foreach ($this->get_settings() as $setting) {
|
||||
$pr->set_var($setting->get_name(), $setting->get_value());
|
||||
}
|
||||
// Add backupid as one more var for processor
|
||||
$pr->set_var(backup::VAR_BACKUPID, $this->get_backupid());
|
||||
|
||||
// Get structure definition
|
||||
// Get restore_path elements array adapting and preparing it for processing
|
||||
$structure = $this->define_structure();
|
||||
if (! $structure instanceof backup_nested_element) {
|
||||
throw new backup_step_exception('backup_structure_step_wrong_structure');
|
||||
if (!is_array($structure)) {
|
||||
throw new restore_step_exception('restore_step_structure_not_array', $this->get_name()); // @todo Change exception
|
||||
}
|
||||
|
||||
// Start writer
|
||||
$xw->start();
|
||||
|
||||
// Process structure definition
|
||||
$structure->process($pr);
|
||||
|
||||
// Close everything
|
||||
$xw->stop();
|
||||
|
||||
// Destroy the structure. It helps PHP 5.2 memory a lot!
|
||||
$structure->destroy();
|
||||
$this->get_converter()->add_structures($this, $structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* As far as backup structure steps are implementing backup_plugin stuff, they need to
|
||||
* As far as restore structure steps are implementing restore_plugin stuff, they need to
|
||||
* have the parent task available for wrapping purposes (get course/context....)
|
||||
*/
|
||||
public function get_task() {
|
||||
@ -81,40 +25,14 @@ abstract class convert_structure_step extends convert_step {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin structure to any element in the structure backup tree
|
||||
* This method will be executed after the whole structure step have been processed
|
||||
*
|
||||
* @param string $plugintype type of plugin as defined by get_plugin_types()
|
||||
* @param backup_nested_element $element element in the structure backup tree that
|
||||
* we are going to add plugin information to
|
||||
* @param bool $multiple to define if multiple plugins can produce information
|
||||
* for each instance of $element (true) or no (false)
|
||||
* After execution method for code needed to be executed after the whole structure
|
||||
* has been processed. Useful for cleaning tasks, files process and others. Simply
|
||||
* overwrite in in your steps if needed
|
||||
*/
|
||||
protected function add_plugin_structure($plugintype, $element, $multiple) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
// Check the requested plugintype is a valid one
|
||||
if (!array_key_exists($plugintype, get_plugin_types($plugintype))) {
|
||||
throw new backup_step_exception('incorrect_plugin_type', $plugintype);
|
||||
}
|
||||
|
||||
// Arrived here, plugin is correct, let's create the optigroup
|
||||
$optigroupname = $plugintype . '_' . $element->get_name() . '_plugin';
|
||||
$optigroup = new backup_optigroup($optigroupname, null, $multiple);
|
||||
$element->add_child($optigroup); // Add optigroup to stay connected since beginning
|
||||
|
||||
// Get all the optigroup_elements, looking across all the plugin dirs
|
||||
$pluginsdirs = get_plugin_list($plugintype);
|
||||
foreach ($pluginsdirs as $name => $plugindir) {
|
||||
$classname = 'backup_' . $plugintype . '_' . $name . '_plugin';
|
||||
$backupfile = $plugindir . '/backup/moodle2/' . $classname . '.class.php';
|
||||
if (file_exists($backupfile)) {
|
||||
require_once($backupfile);
|
||||
$backupplugin = new $classname($plugintype, $name, $optigroup, $this);
|
||||
// Add plugin returned structure to optigroup
|
||||
$backupplugin->define_plugin_structure($element->get_name());
|
||||
}
|
||||
}
|
||||
protected function after_execute() {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,8 +48,8 @@ abstract class convert_structure_step extends convert_step {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that will return the structure to be processed by this backup_step.
|
||||
* Must return one backup_nested_element
|
||||
* Function that will return the structure to be processed by this convert_step.
|
||||
* Must return one array of @convert_path_element elements
|
||||
*/
|
||||
abstract protected function define_structure();
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,11 @@ abstract class convert_task extends base_task {
|
||||
public function get_convertid() {
|
||||
return $this->plan->get_backupid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plan_converter
|
||||
*/
|
||||
public function get_converter() {
|
||||
return $this->plan->get_converter();
|
||||
}
|
||||
}
|
7
backup/util/structure/convert_path_element.class.php
Normal file
7
backup/util/structure/convert_path_element.class.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
class convert_path_element extends restore_path_element {
|
||||
public function get_processing_method() {
|
||||
return 'convert_' . $this->get_name();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user