MDL-21432 backup - restore_structure_step implementation
[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
34
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 }
46
47 public function execute() {
48
49 $fullpath = $this->task->get_taskbasepath();
50
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 }
55
56 // Append the filename to the fullpath
57 $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
58
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 }
63
64 // Get restore_path elements array adapting and preparing it for processing
65 $this->pathelements = $this->prepare_pathelements($this->define_structure());
66
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);
72
73 // Add pathelements to processor
74 foreach ($this->pathelements as $element) {
75 $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
76 }
77
78 // And process it, dispatch to target methods in step will start automatically
79 $xmlparser->process();
80 }
81
82// Protected API starts here
83
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 }
105
106 /**
107 * Prepare the pathelements for processing, looking for duplicates, applying
108 * processing objects and other adjustments
109 */
110 protected function prepare_pathelements($elementsarr) {
111
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 }
137
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 }
153
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();
159}