Updated the HEAD build version to 20100718
[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         if (!$this->execute_condition()) { // Check any condition to execute this
50             return;
51         }
53         $fullpath = $this->task->get_taskbasepath();
55         // We MUST have one fullpath here, else, error
56         if (empty($fullpath)) {
57             throw new restore_step_exception('restore_structure_step_undefined_fullpath');
58         }
60         // Append the filename to the fullpath
61         $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
63         // And it MUST exist
64         if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
65             throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
66         }
68         // Get restore_path elements array adapting and preparing it for processing
69         $this->pathelements = $this->prepare_pathelements($this->define_structure());
71         // Create parser and processor
72         $xmlparser = new progressive_parser();
73         $xmlparser->set_file($fullpath);
74         $xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
75         $xmlparser->set_processor($xmlprocessor);
77         // Add pathelements to processor
78         foreach ($this->pathelements as $element) {
79             $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
80         }
82         // And process it, dispatch to target methods in step will start automatically
83         $xmlparser->process();
84     }
86 // Protected API starts here
88     /**
89      * Receive one chunk of information form the xml parser processor and
90      * dispatch it, following the naming rules
91      */
92     public function process($data) {
93         if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
94             throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
95         }
96         $element = $this->pathelements[$data['path']];
97         $object = $element->get_processing_object();
98         $method = $element->get_processing_method();
99         if (empty($object)) { // No processing object defined
100             throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
101         }
102         $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
103         if ($rdata !== null) { // If the method has returned any info, set element data to it
104             $element->set_data($rdata);
105         } else {               // Else, put the original parsed data
106             $element->set_data($data);
107         }
108     }
110     /**
111      * Prepare the pathelements for processing, looking for duplicates, applying
112      * processing objects and other adjustments
113      */
114     protected function prepare_pathelements($elementsarr) {
116         // First iteration, push them to new array, indexed by name
117         // detecting duplicates in names or paths
118         $names = array();
119         $paths = array();
120         foreach($elementsarr as $element) {
121             if (array_key_exists($element->get_name(), $names)) {
122                 throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
123             }
124             if (array_key_exists($element->get_path(), $paths)) {
125                 throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
126             }
127             $names[$element->get_name()] = true;
128             $elements[$element->get_path()] = $element;
129         }
130         // Now, for each element not having one processing object, if
131         // not child of grouped element, assign $this (the step itself) as processing element
132         // Note method must exist or we'll get one @restore_path_element_exception
133         foreach($elements as $key => $pelement) {
134             if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $elements)) {
135                 $elements[$key]->set_processing_object($this);
136             }
137         }
138         // Done, return them
139         return $elements;
140     }
142     /**
143      * Given one pathelement, return true if grouped parent was found
144      */
145     protected function grouped_parent_exists($pelement, $elements) {
146         foreach ($elements as $element) {
147             if ($pelement->get_path() == $element->get_path()) {
148                 continue; // Don't compare against itself
149             }
150             // If element is grouped and parent of pelement, return true
151             if ($element->is_grouped() and strpos($pelement->get_path() .  '/', $element->get_path()) === 0) {
152                 return true;
153             }
154         }
155         return false; // no grouped parent found
156     }
158     /**
159      * To conditionally decide if one step will be executed or no
160      *
161      * For steps needing to be executed conditionally, based in dynamic
162      * conditions (at execution time vs at declaration time) you must
163      * override this function. It will return true if the step must be
164      * executed and false if not
165      */
166     protected function execute_condition() {
167         return true;
168     }
170     /**
171      * Function that will return the structure to be processed by this restore_step.
172      * Must return one array of @restore_path_element elements
173      */
174     abstract protected function define_structure();