1 |
efrain |
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 |
*/
|
|
|
30 |
abstract class restore_structure_step extends restore_step {
|
|
|
31 |
|
|
|
32 |
protected $filename; // Name of the file to be parsed
|
|
|
33 |
protected $contentprocessor; // xml parser processor being used
|
|
|
34 |
// (need it here, apart from parser
|
|
|
35 |
// thanks to serialized data to process -
|
|
|
36 |
// say thanks to blocks!)
|
|
|
37 |
protected $pathelements; // Array of pathelements to process
|
|
|
38 |
protected $elementsoldid; // Array to store last oldid used on each element
|
|
|
39 |
protected $elementsnewid; // Array to store last newid used on each element
|
|
|
40 |
|
|
|
41 |
protected $pathlock; // Path currently locking processing of children
|
|
|
42 |
|
|
|
43 |
const SKIP_ALL_CHILDREN = -991399; // To instruct the dispatcher about to ignore
|
|
|
44 |
// all children below path processor returning it
|
|
|
45 |
|
|
|
46 |
/**
|
|
|
47 |
* Constructor - instantiates one object of this class
|
|
|
48 |
*/
|
|
|
49 |
public function __construct($name, $filename, $task = null) {
|
|
|
50 |
if (!is_null($task) && !($task instanceof restore_task)) {
|
|
|
51 |
throw new restore_step_exception('wrong_restore_task_specified');
|
|
|
52 |
}
|
|
|
53 |
$this->filename = $filename;
|
|
|
54 |
$this->contentprocessor = null;
|
|
|
55 |
$this->pathelements = array();
|
|
|
56 |
$this->elementsoldid = array();
|
|
|
57 |
$this->elementsnewid = array();
|
|
|
58 |
$this->pathlock = null;
|
|
|
59 |
parent::__construct($name, $task);
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
final public function execute() {
|
|
|
63 |
|
|
|
64 |
if (!$this->execute_condition()) { // Check any condition to execute this
|
|
|
65 |
return;
|
|
|
66 |
}
|
|
|
67 |
|
|
|
68 |
$fullpath = $this->task->get_taskbasepath();
|
|
|
69 |
|
|
|
70 |
// We MUST have one fullpath here, else, error
|
|
|
71 |
if (empty($fullpath)) {
|
|
|
72 |
throw new restore_step_exception('restore_structure_step_undefined_fullpath');
|
|
|
73 |
}
|
|
|
74 |
|
|
|
75 |
// Append the filename to the fullpath
|
|
|
76 |
$fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
|
|
|
77 |
|
|
|
78 |
// And it MUST exist
|
|
|
79 |
if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
|
|
|
80 |
throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
|
|
|
81 |
}
|
|
|
82 |
|
|
|
83 |
// Get restore_path elements array adapting and preparing it for processing
|
|
|
84 |
$structure = $this->define_structure();
|
|
|
85 |
if (!is_array($structure)) {
|
|
|
86 |
throw new restore_step_exception('restore_step_structure_not_array', $this->get_name());
|
|
|
87 |
}
|
|
|
88 |
$this->prepare_pathelements($structure);
|
|
|
89 |
|
|
|
90 |
// Create parser and processor
|
|
|
91 |
$xmlparser = new progressive_parser();
|
|
|
92 |
$xmlparser->set_file($fullpath);
|
|
|
93 |
$xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
|
|
|
94 |
$this->contentprocessor = $xmlprocessor; // Save the reference to the contentprocessor
|
|
|
95 |
// as far as we are going to need it out
|
|
|
96 |
// from parser (blame serialized data!)
|
|
|
97 |
$xmlparser->set_processor($xmlprocessor);
|
|
|
98 |
|
|
|
99 |
// Add pathelements to processor
|
|
|
100 |
foreach ($this->pathelements as $element) {
|
|
|
101 |
$xmlprocessor->add_path($element->get_path(), $element->is_grouped());
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
// Set up progress tracking.
|
|
|
105 |
$progress = $this->get_task()->get_progress();
|
|
|
106 |
$progress->start_progress($this->get_name(), \core\progress\base::INDETERMINATE);
|
|
|
107 |
$xmlparser->set_progress($progress);
|
|
|
108 |
|
|
|
109 |
// And process it, dispatch to target methods in step will start automatically
|
|
|
110 |
$xmlparser->process();
|
|
|
111 |
|
|
|
112 |
// Have finished, launch the after_execute method of all the processing objects
|
|
|
113 |
$this->launch_after_execute_methods();
|
|
|
114 |
$progress->end_progress();
|
|
|
115 |
}
|
|
|
116 |
|
|
|
117 |
/**
|
|
|
118 |
* Receive one chunk of information form the xml parser processor and
|
|
|
119 |
* dispatch it, following the naming rules
|
|
|
120 |
*/
|
|
|
121 |
final public function process($data) {
|
|
|
122 |
if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
|
|
|
123 |
throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
|
|
|
124 |
}
|
|
|
125 |
$element = $this->pathelements[$data['path']];
|
|
|
126 |
$object = $element->get_processing_object();
|
|
|
127 |
$method = $element->get_processing_method();
|
|
|
128 |
$rdata = null;
|
|
|
129 |
if (empty($object)) { // No processing object defined
|
|
|
130 |
throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
|
|
|
131 |
}
|
|
|
132 |
// Release the lock if we aren't anymore within children of it
|
|
|
133 |
if (!is_null($this->pathlock) and strpos($data['path'], $this->pathlock) === false) {
|
|
|
134 |
$this->pathlock = null;
|
|
|
135 |
}
|
|
|
136 |
if (is_null($this->pathlock)) { // Only dispatch if there isn't any lock
|
|
|
137 |
$rdata = $object->$method($data['tags']); // Dispatch to proper object/method
|
|
|
138 |
}
|
|
|
139 |
|
|
|
140 |
// If the dispatched method returns SKIP_ALL_CHILDREN, we grab current path in order to
|
|
|
141 |
// lock dispatching to any children
|
|
|
142 |
if ($rdata === self::SKIP_ALL_CHILDREN) {
|
|
|
143 |
// Check we haven't any previous lock
|
|
|
144 |
if (!is_null($this->pathlock)) {
|
|
|
145 |
throw new restore_step_exception('restore_structure_step_already_skipping', $data['path']);
|
|
|
146 |
}
|
|
|
147 |
// Set the lock
|
|
|
148 |
$this->pathlock = $data['path'] . '/'; // Lock everything below current path
|
|
|
149 |
|
|
|
150 |
// Continue with normal processing of return values
|
|
|
151 |
} else if ($rdata !== null) { // If the method has returned any info, set element data to it
|
|
|
152 |
$element->set_data($rdata);
|
|
|
153 |
} else { // Else, put the original parsed data
|
|
|
154 |
$element->set_data($data);
|
|
|
155 |
}
|
|
|
156 |
}
|
|
|
157 |
|
|
|
158 |
/**
|
|
|
159 |
* To send ids pairs to backup_ids_table and to store them into paths
|
|
|
160 |
*
|
|
|
161 |
* This method will send the given itemname and old/new ids to the
|
|
|
162 |
* backup_ids_temp table, and, at the same time, will save the new id
|
|
|
163 |
* into the corresponding restore_path_element for easier access
|
|
|
164 |
* by children. Also will inject the known old context id for the task
|
|
|
165 |
* in case it's going to be used for restoring files later
|
|
|
166 |
*/
|
|
|
167 |
public function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null, $parentid = null) {
|
|
|
168 |
if ($restorefiles && $parentid) {
|
|
|
169 |
throw new restore_step_exception('set_mapping_cannot_specify_both_restorefiles_and_parentitemid');
|
|
|
170 |
}
|
|
|
171 |
// If we haven't specified one context for the files, use the task one
|
|
|
172 |
if (is_null($filesctxid)) {
|
|
|
173 |
$parentitemid = $restorefiles ? $this->task->get_old_contextid() : null;
|
|
|
174 |
} else { // Use the specified one
|
|
|
175 |
$parentitemid = $restorefiles ? $filesctxid : null;
|
|
|
176 |
}
|
|
|
177 |
// We have passed one explicit parentid, apply it
|
|
|
178 |
$parentitemid = !is_null($parentid) ? $parentid : $parentitemid;
|
|
|
179 |
|
|
|
180 |
// Let's call the low level one
|
|
|
181 |
restore_dbops::set_backup_ids_record($this->get_restoreid(), $itemname, $oldid, $newid, $parentitemid);
|
|
|
182 |
// Now, if the itemname matches any pathelement->name, store the latest $newid
|
|
|
183 |
if (array_key_exists($itemname, $this->elementsoldid)) { // If present in $this->elementsoldid, is valid, put both ids
|
|
|
184 |
$this->elementsoldid[$itemname] = $oldid;
|
|
|
185 |
$this->elementsnewid[$itemname] = $newid;
|
|
|
186 |
}
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
/**
|
|
|
190 |
* Returns the latest (parent) old id mapped by one pathelement
|
|
|
191 |
*/
|
|
|
192 |
public function get_old_parentid($itemname) {
|
|
|
193 |
return array_key_exists($itemname, $this->elementsoldid) ? $this->elementsoldid[$itemname] : null;
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
/**
|
|
|
197 |
* Returns the latest (parent) new id mapped by one pathelement
|
|
|
198 |
*/
|
|
|
199 |
public function get_new_parentid($itemname) {
|
|
|
200 |
return array_key_exists($itemname, $this->elementsnewid) ? $this->elementsnewid[$itemname] : null;
|
|
|
201 |
}
|
|
|
202 |
|
|
|
203 |
/**
|
|
|
204 |
* Return the new id of a mapping for the given itemname
|
|
|
205 |
*
|
|
|
206 |
* @param string $itemname the type of item
|
|
|
207 |
* @param int $oldid the item ID from the backup
|
|
|
208 |
* @param mixed $ifnotfound what to return if $oldid wasnt found. Defaults to false
|
|
|
209 |
*/
|
|
|
210 |
public function get_mappingid($itemname, $oldid, $ifnotfound = false) {
|
|
|
211 |
$mapping = $this->get_mapping($itemname, $oldid);
|
|
|
212 |
return $mapping ? $mapping->newitemid : $ifnotfound;
|
|
|
213 |
}
|
|
|
214 |
|
|
|
215 |
/**
|
|
|
216 |
* Return the complete mapping from the given itemname, itemid
|
|
|
217 |
*/
|
|
|
218 |
public function get_mapping($itemname, $oldid) {
|
|
|
219 |
return restore_dbops::get_backup_ids_record($this->get_restoreid(), $itemname, $oldid);
|
|
|
220 |
}
|
|
|
221 |
|
|
|
222 |
/**
|
|
|
223 |
* Add all the existing file, given their component and filearea and one backup_ids itemname to match with
|
|
|
224 |
*/
|
|
|
225 |
public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
|
|
|
226 |
// If the current progress object is set up and ready to receive
|
|
|
227 |
// indeterminate progress, then use it, otherwise don't. (This check is
|
|
|
228 |
// just in case this function is ever called from somewhere not within
|
|
|
229 |
// the execute() method here, which does set up progress like this.)
|
|
|
230 |
$progress = $this->get_task()->get_progress();
|
|
|
231 |
if (!$progress->is_in_progress_section() ||
|
|
|
232 |
$progress->get_current_max() !== \core\progress\base::INDETERMINATE) {
|
|
|
233 |
$progress = null;
|
|
|
234 |
}
|
|
|
235 |
|
|
|
236 |
$filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid;
|
|
|
237 |
$results = restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
|
|
|
238 |
$filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid, null, false,
|
|
|
239 |
$progress);
|
|
|
240 |
$resultstoadd = array();
|
|
|
241 |
foreach ($results as $result) {
|
|
|
242 |
$this->log($result->message, $result->level);
|
|
|
243 |
$resultstoadd[$result->code] = true;
|
|
|
244 |
}
|
|
|
245 |
$this->task->add_result($resultstoadd);
|
|
|
246 |
}
|
|
|
247 |
|
|
|
248 |
/**
|
|
|
249 |
* As far as restore structure steps are implementing restore_plugin stuff, they need to
|
|
|
250 |
* have the parent task available for wrapping purposes (get course/context....)
|
|
|
251 |
* @return restore_task|null
|
|
|
252 |
*/
|
|
|
253 |
public function get_task() {
|
|
|
254 |
return $this->task;
|
|
|
255 |
}
|
|
|
256 |
|
|
|
257 |
// Protected API starts here
|
|
|
258 |
|
|
|
259 |
/**
|
|
|
260 |
* Add plugin structure to any element in the structure restore tree
|
|
|
261 |
*
|
|
|
262 |
* @param string $plugintype type of plugin as defined by core_component::get_plugin_types()
|
|
|
263 |
* @param restore_path_element $element element in the structure restore tree that
|
|
|
264 |
* we are going to add plugin information to
|
|
|
265 |
*/
|
|
|
266 |
protected function add_plugin_structure($plugintype, $element) {
|
|
|
267 |
|
|
|
268 |
global $CFG;
|
|
|
269 |
|
|
|
270 |
// Check the requested plugintype is a valid one
|
|
|
271 |
if (!array_key_exists($plugintype, core_component::get_plugin_types($plugintype))) {
|
|
|
272 |
throw new restore_step_exception('incorrect_plugin_type', $plugintype);
|
|
|
273 |
}
|
|
|
274 |
|
|
|
275 |
// Get all the restore path elements, looking across all the plugin dirs
|
|
|
276 |
$pluginsdirs = core_component::get_plugin_list($plugintype);
|
|
|
277 |
foreach ($pluginsdirs as $name => $pluginsdir) {
|
|
|
278 |
// We need to add also backup plugin classes on restore, they may contain
|
|
|
279 |
// some stuff used both in backup & restore
|
|
|
280 |
$backupclassname = 'backup_' . $plugintype . '_' . $name . '_plugin';
|
|
|
281 |
$backupfile = $pluginsdir . '/backup/moodle2/' . $backupclassname . '.class.php';
|
|
|
282 |
if (file_exists($backupfile)) {
|
|
|
283 |
require_once($backupfile);
|
|
|
284 |
}
|
|
|
285 |
// Now add restore plugin classes and prepare stuff
|
|
|
286 |
$restoreclassname = 'restore_' . $plugintype . '_' . $name . '_plugin';
|
|
|
287 |
$restorefile = $pluginsdir . '/backup/moodle2/' . $restoreclassname . '.class.php';
|
|
|
288 |
if (file_exists($restorefile)) {
|
|
|
289 |
require_once($restorefile);
|
|
|
290 |
$restoreplugin = new $restoreclassname($plugintype, $name, $this);
|
|
|
291 |
// Add plugin paths to the step
|
|
|
292 |
$this->prepare_pathelements($restoreplugin->define_plugin_structure($element));
|
|
|
293 |
}
|
|
|
294 |
}
|
|
|
295 |
}
|
|
|
296 |
|
|
|
297 |
/**
|
|
|
298 |
* Add subplugin structure for a given plugin to any element in the structure restore tree
|
|
|
299 |
*
|
|
|
300 |
* This method allows the injection of subplugins (of a specific plugin) parsing and proccessing
|
|
|
301 |
* to any element in the restore structure.
|
|
|
302 |
*
|
|
|
303 |
* NOTE: Initially subplugins were only available for activities (mod), so only the
|
|
|
304 |
* {@link restore_activity_structure_step} class had support for them, always
|
|
|
305 |
* looking for /mod/modulenanme subplugins. This new method is a generalization of the
|
|
|
306 |
* existing one for activities, supporting all subplugins injecting information everywhere.
|
|
|
307 |
*
|
|
|
308 |
* @param string $subplugintype type of subplugin as defined in plugin's db/subplugins.json.
|
|
|
309 |
* @param restore_path_element $element element in the structure restore tree that
|
|
|
310 |
* we are going to add subplugin information to.
|
|
|
311 |
* @param string $plugintype type of the plugin.
|
|
|
312 |
* @param string $pluginname name of the plugin.
|
|
|
313 |
* @return void
|
|
|
314 |
*/
|
|
|
315 |
protected function add_subplugin_structure($subplugintype, $element, $plugintype = null, $pluginname = null) {
|
|
|
316 |
global $CFG;
|
|
|
317 |
// This global declaration is required, because where we do require_once($backupfile);
|
|
|
318 |
// That file may in turn try to do require_once($CFG->dirroot ...).
|
|
|
319 |
// That worked in the past, we should keep it working.
|
|
|
320 |
|
|
|
321 |
// Verify if this is a BC call for an activity restore. See NOTE above for this special case.
|
|
|
322 |
if ($plugintype === null and $pluginname === null) {
|
|
|
323 |
$plugintype = 'mod';
|
|
|
324 |
$pluginname = $this->task->get_modulename();
|
|
|
325 |
// TODO: Once all the calls have been changed to add both not null plugintype and pluginname, add a debugging here.
|
|
|
326 |
}
|
|
|
327 |
|
|
|
328 |
// Check the requested plugintype is a valid one.
|
|
|
329 |
if (!array_key_exists($plugintype, core_component::get_plugin_types())) {
|
|
|
330 |
throw new restore_step_exception('incorrect_plugin_type', $plugintype);
|
|
|
331 |
}
|
|
|
332 |
|
|
|
333 |
// Check the requested pluginname, for the specified plugintype, is a valid one.
|
|
|
334 |
if (!array_key_exists($pluginname, core_component::get_plugin_list($plugintype))) {
|
|
|
335 |
throw new restore_step_exception('incorrect_plugin_name', array($plugintype, $pluginname));
|
|
|
336 |
}
|
|
|
337 |
|
|
|
338 |
// Check the requested subplugintype is a valid one.
|
|
|
339 |
$subplugins = core_component::get_subplugins("{$plugintype}_{$pluginname}");
|
|
|
340 |
if (null === $subplugins) {
|
|
|
341 |
throw new restore_step_exception('plugin_missing_subplugins_configuration', array($plugintype, $pluginname));
|
|
|
342 |
}
|
|
|
343 |
if (!array_key_exists($subplugintype, $subplugins)) {
|
|
|
344 |
throw new restore_step_exception('incorrect_subplugin_type', $subplugintype);
|
|
|
345 |
}
|
|
|
346 |
|
|
|
347 |
// Every subplugin optionally can have a common/parent subplugin
|
|
|
348 |
// class for shared stuff.
|
|
|
349 |
$parentclass = 'restore_' . $plugintype . '_' . $pluginname . '_' . $subplugintype . '_subplugin';
|
|
|
350 |
$parentfile = core_component::get_component_directory($plugintype . '_' . $pluginname) .
|
|
|
351 |
'/backup/moodle2/' . $parentclass . '.class.php';
|
|
|
352 |
if (file_exists($parentfile)) {
|
|
|
353 |
require_once($parentfile);
|
|
|
354 |
}
|
|
|
355 |
|
|
|
356 |
// Get all the restore path elements, looking across all the subplugin dirs.
|
|
|
357 |
$subpluginsdirs = core_component::get_plugin_list($subplugintype);
|
|
|
358 |
foreach ($subpluginsdirs as $name => $subpluginsdir) {
|
|
|
359 |
$classname = 'restore_' . $subplugintype . '_' . $name . '_subplugin';
|
|
|
360 |
$restorefile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
|
|
|
361 |
if (file_exists($restorefile)) {
|
|
|
362 |
require_once($restorefile);
|
|
|
363 |
$restoresubplugin = new $classname($subplugintype, $name, $this);
|
|
|
364 |
// Add subplugin paths to the step.
|
|
|
365 |
$this->prepare_pathelements($restoresubplugin->define_subplugin_structure($element));
|
|
|
366 |
}
|
|
|
367 |
}
|
|
|
368 |
}
|
|
|
369 |
|
|
|
370 |
/**
|
|
|
371 |
* Launch all the after_execute methods present in all the processing objects
|
|
|
372 |
*
|
|
|
373 |
* This method will launch all the after_execute methods that can be defined
|
|
|
374 |
* both in restore_plugin and restore_structure_step classes
|
|
|
375 |
*
|
|
|
376 |
* For restore_plugin classes the name of the method to be executed will be
|
|
|
377 |
* "after_execute_" + connection point (as far as can be multiple connection
|
|
|
378 |
* points in the same class)
|
|
|
379 |
*
|
|
|
380 |
* For restore_structure_step classes is will be, simply, "after_execute". Note
|
|
|
381 |
* that this is executed *after* the plugin ones
|
|
|
382 |
*/
|
|
|
383 |
protected function launch_after_execute_methods() {
|
|
|
384 |
$alreadylaunched = array(); // To avoid multiple executions
|
|
|
385 |
foreach ($this->pathelements as $key => $pathelement) {
|
|
|
386 |
// Get the processing object
|
|
|
387 |
$pobject = $pathelement->get_processing_object();
|
|
|
388 |
// Skip null processors (child of grouped ones for sure)
|
|
|
389 |
if (is_null($pobject)) {
|
|
|
390 |
continue;
|
|
|
391 |
}
|
|
|
392 |
// Skip restore structure step processors (this)
|
|
|
393 |
if ($pobject instanceof restore_structure_step) {
|
|
|
394 |
continue;
|
|
|
395 |
}
|
|
|
396 |
// Skip already launched processing objects
|
|
|
397 |
if (in_array($pobject, $alreadylaunched, true)) {
|
|
|
398 |
continue;
|
|
|
399 |
}
|
|
|
400 |
// Add processing object to array of launched ones
|
|
|
401 |
$alreadylaunched[] = $pobject;
|
|
|
402 |
// If the processing object has support for
|
|
|
403 |
// launching after_execute methods, use it
|
|
|
404 |
if (method_exists($pobject, 'launch_after_execute_methods')) {
|
|
|
405 |
$pobject->launch_after_execute_methods();
|
|
|
406 |
}
|
|
|
407 |
}
|
|
|
408 |
// Finally execute own (restore_structure_step) after_execute method
|
|
|
409 |
$this->after_execute();
|
|
|
410 |
|
|
|
411 |
}
|
|
|
412 |
|
|
|
413 |
/**
|
|
|
414 |
* Launch all the after_restore methods present in all the processing objects
|
|
|
415 |
*
|
|
|
416 |
* This method will launch all the after_restore methods that can be defined
|
|
|
417 |
* both in restore_plugin class
|
|
|
418 |
*
|
|
|
419 |
* For restore_plugin classes the name of the method to be executed will be
|
|
|
420 |
* "after_restore_" + connection point (as far as can be multiple connection
|
|
|
421 |
* points in the same class)
|
|
|
422 |
*/
|
|
|
423 |
public function launch_after_restore_methods() {
|
|
|
424 |
$alreadylaunched = array(); // To avoid multiple executions
|
|
|
425 |
foreach ($this->pathelements as $pathelement) {
|
|
|
426 |
// Get the processing object
|
|
|
427 |
$pobject = $pathelement->get_processing_object();
|
|
|
428 |
// Skip null processors (child of grouped ones for sure)
|
|
|
429 |
if (is_null($pobject)) {
|
|
|
430 |
continue;
|
|
|
431 |
}
|
|
|
432 |
// Skip restore structure step processors (this)
|
|
|
433 |
if ($pobject instanceof restore_structure_step) {
|
|
|
434 |
continue;
|
|
|
435 |
}
|
|
|
436 |
// Skip already launched processing objects
|
|
|
437 |
if (in_array($pobject, $alreadylaunched, true)) {
|
|
|
438 |
continue;
|
|
|
439 |
}
|
|
|
440 |
// Add processing object to array of launched ones
|
|
|
441 |
$alreadylaunched[] = $pobject;
|
|
|
442 |
// If the processing object has support for
|
|
|
443 |
// launching after_restore methods, use it
|
|
|
444 |
if (method_exists($pobject, 'launch_after_restore_methods')) {
|
|
|
445 |
$pobject->launch_after_restore_methods();
|
|
|
446 |
}
|
|
|
447 |
}
|
|
|
448 |
// Finally execute own (restore_structure_step) after_restore method
|
|
|
449 |
$this->after_restore();
|
|
|
450 |
}
|
|
|
451 |
|
|
|
452 |
/**
|
|
|
453 |
* This method will be executed after the whole structure step have been processed
|
|
|
454 |
*
|
|
|
455 |
* After execution method for code needed to be executed after the whole structure
|
|
|
456 |
* has been processed. Useful for cleaning tasks, files process and others. Simply
|
|
|
457 |
* overwrite in in your steps if needed
|
|
|
458 |
*/
|
|
|
459 |
protected function after_execute() {
|
|
|
460 |
// do nothing by default
|
|
|
461 |
}
|
|
|
462 |
|
|
|
463 |
/**
|
|
|
464 |
* This method will be executed after the rest of the restore has been processed.
|
|
|
465 |
*
|
|
|
466 |
* Use if you need to update IDs based on things which are restored after this
|
|
|
467 |
* step has completed.
|
|
|
468 |
*/
|
|
|
469 |
protected function after_restore() {
|
|
|
470 |
// do nothing by default
|
|
|
471 |
}
|
|
|
472 |
|
|
|
473 |
/**
|
|
|
474 |
* Prepare the pathelements for processing, looking for duplicates, applying
|
|
|
475 |
* processing objects and other adjustments
|
|
|
476 |
*/
|
|
|
477 |
protected function prepare_pathelements($elementsarr) {
|
|
|
478 |
|
|
|
479 |
// First iteration, push them to new array, indexed by name
|
|
|
480 |
// detecting duplicates in names or paths
|
|
|
481 |
$names = array();
|
|
|
482 |
$paths = array();
|
|
|
483 |
foreach($elementsarr as $element) {
|
|
|
484 |
if (!$element instanceof restore_path_element) {
|
|
|
485 |
throw new restore_step_exception('restore_path_element_wrong_class', get_class($element));
|
|
|
486 |
}
|
|
|
487 |
if (array_key_exists($element->get_name(), $names)) {
|
|
|
488 |
throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
|
|
|
489 |
}
|
|
|
490 |
if (array_key_exists($element->get_path(), $paths)) {
|
|
|
491 |
throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
|
|
|
492 |
}
|
|
|
493 |
$names[$element->get_name()] = true;
|
|
|
494 |
$paths[$element->get_path()] = $element;
|
|
|
495 |
}
|
|
|
496 |
// Now, for each element not having one processing object, if
|
|
|
497 |
// not child of grouped element, assign $this (the step itself) as processing element
|
|
|
498 |
// Note method must exist or we'll get one @restore_path_element_exception
|
|
|
499 |
foreach ($paths as $pelement) {
|
|
|
500 |
if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $paths)) {
|
|
|
501 |
$pelement->set_processing_object($this);
|
|
|
502 |
}
|
|
|
503 |
// Populate $elementsoldid and $elementsoldid based on available pathelements
|
|
|
504 |
$this->elementsoldid[$pelement->get_name()] = null;
|
|
|
505 |
$this->elementsnewid[$pelement->get_name()] = null;
|
|
|
506 |
}
|
|
|
507 |
// Done, add them to pathelements (dupes by key - path - are discarded)
|
|
|
508 |
$this->pathelements = array_merge($this->pathelements, $paths);
|
|
|
509 |
}
|
|
|
510 |
|
|
|
511 |
/**
|
|
|
512 |
* Given one pathelement, return true if grouped parent was found
|
|
|
513 |
*
|
|
|
514 |
* @param restore_path_element $pelement the element we are interested in.
|
|
|
515 |
* @param restore_path_element[] $elements the elements that exist.
|
|
|
516 |
* @return bool true if this element is inside a grouped parent.
|
|
|
517 |
*/
|
|
|
518 |
public function grouped_parent_exists($pelement, $elements) {
|
|
|
519 |
foreach ($elements as $element) {
|
|
|
520 |
if ($pelement->get_path() == $element->get_path()) {
|
|
|
521 |
continue; // Don't compare against itself.
|
|
|
522 |
}
|
|
|
523 |
// If element is grouped and parent of pelement, return true.
|
|
|
524 |
if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) {
|
|
|
525 |
return true;
|
|
|
526 |
}
|
|
|
527 |
}
|
|
|
528 |
return false; // No grouped parent found.
|
|
|
529 |
}
|
|
|
530 |
|
|
|
531 |
/**
|
|
|
532 |
* To conditionally decide if one step will be executed or no
|
|
|
533 |
*
|
|
|
534 |
* For steps needing to be executed conditionally, based in dynamic
|
|
|
535 |
* conditions (at execution time vs at declaration time) you must
|
|
|
536 |
* override this function. It will return true if the step must be
|
|
|
537 |
* executed and false if not
|
|
|
538 |
*/
|
|
|
539 |
protected function execute_condition() {
|
|
|
540 |
return true;
|
|
|
541 |
}
|
|
|
542 |
|
|
|
543 |
/**
|
|
|
544 |
* Function that will return the structure to be processed by this restore_step.
|
|
|
545 |
* Must return one array of @restore_path_element elements
|
|
|
546 |
*/
|
|
|
547 |
abstract protected function define_structure();
|
|
|
548 |
}
|