MDL-21432 backup - restore_structure_step implementation
[moodle.git] / backup / util / plan / restore_structure_step.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package moodlecore
20  * @subpackage backup-plan
21  * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 /**
26  * Abstract class defining the needed stuff to restore one xml file
27  *
28  * TODO: Finish phpdocs
29  */
30 abstract class restore_structure_step extends restore_step {
32     protected $filename; // Name of the file to be parsed
33     protected $pathelements; // Array of pathelements to process
35     /**
36      * Constructor - instantiates one object of this class
37      */
38     public function __construct($name, $filename, $task = null) {
39         if (!is_null($task) && !($task instanceof restore_task)) {
40             throw new restore_step_exception('wrong_restore_task_specified');
41         }
42         $this->filename = $filename;
43         $this->pathelements = array();
44         parent::__construct($name, $task);
45     }
47     public function execute() {
49         $fullpath = $this->task->get_taskbasepath();
51         // We MUST have one fullpath here, else, error
52         if (empty($fullpath)) {
53             throw new restore_step_exception('restore_structure_step_undefined_fullpath');
54         }
56         // Append the filename to the fullpath
57         $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
59         // And it MUST exist
60         if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
61             throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
62         }
64         // Get restore_path elements array adapting and preparing it for processing
65         $this->pathelements = $this->prepare_pathelements($this->define_structure());
67         // Create parser and processor
68         $xmlparser = new progressive_parser();
69         $xmlparser->set_file($fullpath);
70         $xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
71         $xmlparser->set_processor($xmlprocessor);
73         // Add pathelements to processor
74         foreach ($this->pathelements as $element) {
75             $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
76         }
78         // And process it, dispatch to target methods in step will start automatically
79         $xmlparser->process();
80     }
82 // Protected API starts here
84     /**
85      * Receive one chunk of information form the xml parser processor and
86      * dispatch it, following the naming rules
87      */
88     public function process($data) {
89         if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
90             throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
91         }
92         $element = $this->pathelements[$data['path']];
93         $object = $element->get_processing_object();
94         $method = $element->get_processing_method();
95         if (empty($object)) { // No processing object defined
96             throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
97         }
98         $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
99         if ($rdata !== null) { // If the method has returned any info, set element data to it
100             $element->set_data($rdata);
101         } else {               // Else, put the original parsed data
102             $element->set_data($data);
103         }
104     }
106     /**
107      * Prepare the pathelements for processing, looking for duplicates, applying
108      * processing objects and other adjustments
109      */
110     protected function prepare_pathelements($elementsarr) {
112         // First iteration, push them to new array, indexed by name
113         // detecting duplicates in names or paths
114         $names = array();
115         $paths = array();
116         foreach($elementsarr as $element) {
117             if (array_key_exists($element->get_name(), $names)) {
118                 throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
119             }
120             if (array_key_exists($element->get_path(), $paths)) {
121                 throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
122             }
123             $names[$element->get_name()] = true;
124             $elements[$element->get_path()] = $element;
125         }
126         // Now, for each element not having one processing object, if
127         // not child of grouped element, assign $this (the step itself) as processing element
128         // Note method must exist or we'll get one @restore_path_element_exception
129         foreach($elements as $key => $pelement) {
130             if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $elements)) {
131                 $elements[$key]->set_processing_object($this);
132             }
133         }
134         // Done, return them
135         return $elements;
136     }
138     /**
139      * Given one pathelement, return true if grouped parent was found
140      */
141     protected function grouped_parent_exists($pelement, $elements) {
142         foreach ($elements as $element) {
143             if ($pelement->get_path() == $element->get_path()) {
144                 continue; // Don't compare against itself
145             }
146             // If element is grouped and parent of pelement, return true
147             if ($element->is_grouped() and strpos($pelement->get_path() .  '/', $element->get_path()) === 0) {
148                 return true;
149             }
150         }
151         return false; // no grouped parent found
152     }
154     /**
155      * Function that will return the structure to be processed by this restore_step.
156      * Must return one array of @restore_path_element elements
157      */
158     abstract protected function define_structure();