MDL-22903 Thanks for the catch about groupings on front page, Paul Nicholls!
[moodle.git] / backup / util / plan / restore_structure_step.class.php
CommitLineData
69023455
EL
1<?php
2
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/>.
17
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 */
24
25/**
26 * Abstract class defining the needed stuff to restore one xml file
27 *
28 * TODO: Finish phpdocs
29 */
30abstract class restore_structure_step extends restore_step {
31
32 protected $filename; // Name of the file to be parsed
33 protected $pathelements; // Array of pathelements to process
2df0f295
EL
34 protected $elementsoldid; // Array to store last oldid used on each element
35 protected $elementsnewid; // Array to store last newid used on each element
69023455
EL
36
37 /**
38 * Constructor - instantiates one object of this class
39 */
40 public function __construct($name, $filename, $task = null) {
41 if (!is_null($task) && !($task instanceof restore_task)) {
42 throw new restore_step_exception('wrong_restore_task_specified');
43 }
44 $this->filename = $filename;
45 $this->pathelements = array();
2df0f295
EL
46 $this->elementsoldid = array();
47 $this->elementsnewid = array();
69023455
EL
48 parent::__construct($name, $task);
49 }
50
51 public function execute() {
52
482aac65
EL
53 if (!$this->execute_condition()) { // Check any condition to execute this
54 return;
55 }
56
69023455
EL
57 $fullpath = $this->task->get_taskbasepath();
58
59 // We MUST have one fullpath here, else, error
60 if (empty($fullpath)) {
61 throw new restore_step_exception('restore_structure_step_undefined_fullpath');
62 }
63
64 // Append the filename to the fullpath
65 $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
66
67 // And it MUST exist
68 if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
69 throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
70 }
71
72 // Get restore_path elements array adapting and preparing it for processing
73 $this->pathelements = $this->prepare_pathelements($this->define_structure());
74
2df0f295
EL
75 // Populate $elementsoldid and $elementsoldid based on available pathelements
76 foreach ($this->pathelements as $pathelement) {
77 $this->elementsoldid[$pathelement->get_name()] = null;
78 $this->elementsnewid[$pathelement->get_name()] = null;
79 }
80
69023455
EL
81 // Create parser and processor
82 $xmlparser = new progressive_parser();
83 $xmlparser->set_file($fullpath);
84 $xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
85 $xmlparser->set_processor($xmlprocessor);
86
87 // Add pathelements to processor
88 foreach ($this->pathelements as $element) {
89 $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
90 }
91
92 // And process it, dispatch to target methods in step will start automatically
93 $xmlparser->process();
69023455 94
2df0f295
EL
95 // Have finished, call to the after_execute method
96 $this->after_execute();
97 }
69023455
EL
98
99 /**
100 * Receive one chunk of information form the xml parser processor and
101 * dispatch it, following the naming rules
102 */
103 public function process($data) {
104 if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
105 throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
106 }
107 $element = $this->pathelements[$data['path']];
108 $object = $element->get_processing_object();
109 $method = $element->get_processing_method();
110 if (empty($object)) { // No processing object defined
111 throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
112 }
113 $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
114 if ($rdata !== null) { // If the method has returned any info, set element data to it
115 $element->set_data($rdata);
116 } else { // Else, put the original parsed data
117 $element->set_data($data);
118 }
119 }
120
2df0f295
EL
121// Protected API starts here
122
123 /**
124 * This method will be executed after the whole structure step have been processed
125 *
126 * After execution method for code needed to be executed after the whole structure
127 * has been processed. Useful for cleaning tasks, files process and others. Simply
128 * overwrite in in your steps if needed
129 */
130 protected function after_execute() {
131 // do nothing by default
132 }
133
69023455
EL
134 /**
135 * Prepare the pathelements for processing, looking for duplicates, applying
136 * processing objects and other adjustments
137 */
138 protected function prepare_pathelements($elementsarr) {
139
140 // First iteration, push them to new array, indexed by name
141 // detecting duplicates in names or paths
142 $names = array();
143 $paths = array();
144 foreach($elementsarr as $element) {
145 if (array_key_exists($element->get_name(), $names)) {
146 throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
147 }
148 if (array_key_exists($element->get_path(), $paths)) {
149 throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
150 }
151 $names[$element->get_name()] = true;
152 $elements[$element->get_path()] = $element;
153 }
154 // Now, for each element not having one processing object, if
155 // not child of grouped element, assign $this (the step itself) as processing element
156 // Note method must exist or we'll get one @restore_path_element_exception
157 foreach($elements as $key => $pelement) {
158 if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $elements)) {
159 $elements[$key]->set_processing_object($this);
160 }
161 }
162 // Done, return them
163 return $elements;
164 }
165
166 /**
167 * Given one pathelement, return true if grouped parent was found
168 */
169 protected function grouped_parent_exists($pelement, $elements) {
170 foreach ($elements as $element) {
171 if ($pelement->get_path() == $element->get_path()) {
172 continue; // Don't compare against itself
173 }
174 // If element is grouped and parent of pelement, return true
175 if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) {
176 return true;
177 }
178 }
179 return false; // no grouped parent found
180 }
181
482aac65
EL
182 /**
183 * To conditionally decide if one step will be executed or no
184 *
185 * For steps needing to be executed conditionally, based in dynamic
186 * conditions (at execution time vs at declaration time) you must
187 * override this function. It will return true if the step must be
188 * executed and false if not
189 */
190 protected function execute_condition() {
191 return true;
192 }
193
2df0f295
EL
194 /**
195 * To send ids pairs to backup_ids_table and to store them into paths
196 *
197 * This method will send the given itemname and old/new ids to the
198 * backup_ids_temp table, and, at the same time, will save the new id
199 * into the corresponding restore_path_element for easier access
200 * by children. Also will inject the known old context id for the task
201 * in case it's going to be used for restoring files later
202 */
c0440b3f
EL
203 protected function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null) {
204 // If we haven't specified one context for the files, use the task one
205 if ($filesctxid == null) {
206 $parentitemid = $restorefiles ? $this->task->get_old_contextid() : null;
207 } else { // Use the specified one
208 $parentitemid = $restorefiles ? $filesctxid : null;
209 }
2df0f295
EL
210 // Let's call the low level one
211 restore_dbops::set_backup_ids_record($this->get_restoreid(), $itemname, $oldid, $newid, $parentitemid);
212 // Now, if the itemname matches any pathelement->name, store the latest $newid
213 if (array_key_exists($itemname, $this->elementsoldid)) { // If present in $this->elementsoldid, is valid, put both ids
214 $this->elementsoldid[$itemname] = $oldid;
215 $this->elementsnewid[$itemname] = $newid;
216 }
217 }
218
219 /**
220 * Returns the latest (parent) old id mapped by one pathelement
221 */
222 protected function get_old_parentid($itemname) {
223 return array_key_exists($itemname, $this->elementsoldid) ? $this->elementsoldid[$itemname] : null;
224 }
225
226 /**
227 * Returns the latest (parent) new id mapped by one pathelement
228 */
229 protected function get_new_parentid($itemname) {
230 return array_key_exists($itemname, $this->elementsnewid) ? $this->elementsnewid[$itemname] : null;
231 }
232
233 /**
234 * Return the new id of a mapping for the given itemname
235 *
236 */
237 protected function get_mappingid($itemname, $oldid) {
238 $mapping = $this->get_mapping($itemname, $oldid);
239 return $mapping ? $mapping->newitemid : false;
240 }
241
242 /**
243 * Return the complete mapping from the given itemname, itemid
244 */
245 protected function get_mapping($itemname, $oldid) {
246 return restore_dbops::get_backup_ids_record($this->get_restoreid(), $itemname, $oldid);
247 }
248
249 /**
250 * Add all the existing file, given their component and filearea and one backup_ids itemname to match with
251 */
c0440b3f
EL
252 protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null) {
253 $filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid;
b8bb45b0 254 restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
c0440b3f 255 $filearea, $filesctxid, $mappingitemname);
2df0f295
EL
256 }
257
69023455
EL
258 /**
259 * Function that will return the structure to be processed by this restore_step.
260 * Must return one array of @restore_path_element elements
261 */
262 abstract protected function define_structure();
263}