MDL-21432 backup - restore_structure_step implementation
authorEloy Lafuente <stronk7@moodle.org>
Mon, 12 Jul 2010 22:44:46 +0000 (22:44 +0000)
committerEloy Lafuente <stronk7@moodle.org>
Mon, 12 Jul 2010 22:44:46 +0000 (22:44 +0000)
backup/util/helper/restore_structure_parser_processor.class.php [new file with mode: 0644]
backup/util/plan/restore_structure_step.class.php [new file with mode: 0644]

diff --git a/backup/util/helper/restore_structure_parser_processor.class.php b/backup/util/helper/restore_structure_parser_processor.class.php
new file mode 100644 (file)
index 0000000..5a28b8f
--- /dev/null
@@ -0,0 +1,79 @@
+<?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 moodlecore
+ * @subpackage backup-helper
+ * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot.'/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
+
+/**
+ * helper implementation of grouped_parser_processor that will
+ * support the process of all the moodle2 backup files, with
+ * complete specs about what to load (grouped or no), dispatching
+ * to corresponding methods and basic decoding of contents
+ * (NULLs and legacy file.php uses)
+ *
+ * TODO: Complete phpdocs
+ */
+class restore_structure_parser_processor extends grouped_parser_processor {
+
+    protected $courseid; // Course->id we are restoring to
+    protected $step;     // @restore_structure_step using this processor
+
+    public function __construct($courseid, $step) {
+        $this->courseid = $courseid;
+        $this->step     = $step;
+        parent::__construct();
+    }
+
+    /**
+     * Provide NULL and legacy file.php uses decoding
+     */
+    public function process_cdata($cdata) {
+        if (is_null($cdata)) {  // 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, $content);
+        // Now $@SLASH@$ and $@FORCEDOWNLOAD@$ MDL-18799
+        $search = array('$@SLASH@$', '$@FORCEDOWNLOAD@$');
+        if ($CFG->slasharguments) {
+            $replace = array('/', '?forcedownload=1');
+        } else {
+            $replace = array('%2F', '&amp;forcedownload=1');
+        }
+        return str_replace($search, $replace, $result);
+    }
+
+    protected function dispatch_chunk($data) {
+        $this->step->process($data);
+    }
+}
diff --git a/backup/util/plan/restore_structure_step.class.php b/backup/util/plan/restore_structure_step.class.php
new file mode 100644 (file)
index 0000000..bfc6210
--- /dev/null
@@ -0,0 +1,159 @@
+<?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 moodlecore
+ * @subpackage backup-plan
+ * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Abstract class defining the needed stuff to restore one xml file
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class restore_structure_step extends restore_step {
+
+    protected $filename; // Name of the file to be parsed
+    protected $pathelements; // Array of pathelements to process
+
+    /**
+     * Constructor - instantiates one object of this class
+     */
+    public function __construct($name, $filename, $task = null) {
+        if (!is_null($task) && !($task instanceof restore_task)) {
+            throw new restore_step_exception('wrong_restore_task_specified');
+        }
+        $this->filename = $filename;
+        $this->pathelements = array();
+        parent::__construct($name, $task);
+    }
+
+    public function execute() {
+
+        $fullpath = $this->task->get_taskbasepath();
+
+        // We MUST have one fullpath here, else, error
+        if (empty($fullpath)) {
+            throw new restore_step_exception('restore_structure_step_undefined_fullpath');
+        }
+
+        // Append the filename to the fullpath
+        $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
+
+        // And it MUST exist
+        if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
+            throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
+        }
+
+        // Get restore_path elements array adapting and preparing it for processing
+        $this->pathelements = $this->prepare_pathelements($this->define_structure());
+
+        // Create parser and processor
+        $xmlparser = new progressive_parser();
+        $xmlparser->set_file($fullpath);
+        $xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
+        $xmlparser->set_processor($xmlprocessor);
+
+        // Add pathelements to processor
+        foreach ($this->pathelements as $element) {
+            $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
+        }
+
+        // And process it, dispatch to target methods in step will start automatically
+        $xmlparser->process();
+    }
+
+// Protected API starts here
+
+    /**
+     * 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']);
+        }
+        $element = $this->pathelements[$data['path']];
+        $object = $element->get_processing_object();
+        $method = $element->get_processing_method();
+        if (empty($object)) { // No processing object defined
+            throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
+        }
+        $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
+        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);
+        }
+    }
+
+    /**
+     * Prepare the pathelements for processing, looking for duplicates, applying
+     * processing objects and other adjustments
+     */
+    protected function prepare_pathelements($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 (array_key_exists($element->get_name(), $names)) {
+                throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
+            }
+            if (array_key_exists($element->get_path(), $paths)) {
+                throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
+            }
+            $names[$element->get_name()] = true;
+            $elements[$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($elements as $key => $pelement) {
+            if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $elements)) {
+                $elements[$key]->set_processing_object($this);
+            }
+        }
+        // Done, return them
+        return $elements;
+    }
+
+    /**
+     * 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
+    }
+
+    /**
+     * Function that will return the structure to be processed by this restore_step.
+     * Must return one array of @restore_path_element elements
+     */
+    abstract protected function define_structure();
+}