MDL-21432 backup - allow arbitrary contexts when annotating files. Record orignal...
[moodle.git] / backup / util / structure / backup_nested_element.class.php
CommitLineData
69dd0c8c
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-structure
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 * TODO: Finish phpdocs
25 */
26
27/**
28 * Instantiable class representing one nestable element (non final) piece of information on backup
29 */
30class backup_nested_element extends base_nested_element implements processable {
31
32 protected $var_array; // To be used in case we pass one in-memory structure
33 protected $table; // Table (without prefix) to fetch records from
34 protected $sql; // Raw SQL to fetch records from
35 protected $params; // Unprocessed params as specified in the set_source() call
36 protected $procparams;// Processed (path resolved) params array
37 protected $aliases; // Define DB->final element aliases
64f93798 38 protected $fileannotations; // array of file areas to be searched by file annotations
69dd0c8c
EL
39 protected $counter; // Number of instances of this element that have been processed
40
41 /**
42 * Constructor - instantiates one backup_nested_element, specifying its basic info.
43 *
44 * @param string $name name of the element
45 * @param array $attributes attributes this element will handle (optional, defaults to null)
46 * @param array $final_elements this element will handle (optional, defaults to null)
47 */
48 public function __construct($name, $attributes = null, $final_elements = null) {
49 parent::__construct($name, $attributes, $final_elements);
50 $this->var_array = null;
51 $this->table = null;
52 $this->sql = null;
53 $this->params = null;
54 $this->procparams= null;
55 $this->aliases = array();
64f93798 56 $this->fileannotations = array();
69dd0c8c
EL
57 $this->counter = 0;
58 }
59
60 public function process($processor) {
61 if (!$processor instanceof base_processor) { // No correct processor, throw exception
62 throw new base_element_struct_exception('incorrect_processor');
63 }
64
65 $iterator = $this->get_iterator($processor); // Get the iterator over backup-able data
66
67 foreach ($iterator as $key => $values) { // Process each "ocurrrence" of the nested element (recordset or array)
68
69 // Fill the values of the attributes and final elements with the $values from the iterator
70 $this->fill_values($values);
71
72 // Perform pre-process tasks for the nested_element
73 $processor->pre_process_nested_element($this);
74
75 // Delegate the process of each attribute
76 foreach ($this->get_attributes() as $attribute) {
77 $attribute->process($processor);
78 }
79
80 // Main process tasks for the nested element, once its attributes have been processed
81 $processor->process_nested_element($this);
82
83 // Delegate the process of each final_element
84 foreach ($this->get_final_elements() as $final_element) {
85 $final_element->process($processor);
86 }
87
88 // Delegate the process to the optigroup
89 if ($this->get_optigroup()) {
90 $this->get_optigroup()->process($processor);
91 }
92
93 // Delegate the process to each child nested_element
94 foreach ($this->get_children() as $child) {
95 $child->process($processor);
96 }
97
98 // Perform post-process tasks for the nested element
99 $processor->post_process_nested_element($this);
100
101 // Everything processed, clean values before next iteration
102 $this->clean_values();
103
104 // Increment counter for this element
105 $this->counter++;
106
107 // For root element, check we only have 1 element
108 if ($this->get_parent() === null && $this->counter > 1) {
109 throw new base_element_struct_exception('root_only_one_ocurrence', $this->get_name());
110 }
111 }
112 // Close the iterator (DB recordset / array iterator)
113 $iterator->close();
114 }
115
116 public function set_source_array($arr) {
117 // TODO: Only elements having final elements can set source
118 $this->var_array = $arr;
119 }
120
121 public function set_source_table($table, $params) {
122 if (!is_array($params)) { // Check we are passing array
123 throw new base_element_struct_exception('setsourcerequiresarrayofparams');
124 }
125 // TODO: Only elements having final elements can set source
126 $this->table = $table;
127 $this->procparams = $this->convert_table_params($params);
128 }
129
130 public function set_source_sql($sql, $params) {
131 if (!is_array($params)) { // Check we are passing array
132 throw new base_element_struct_exception('setsourcerequiresarrayofparams');
133 }
134 // TODO: Only elements having final elements can set source
135 $this->sql = $sql;
136 $this->procparams = $this->convert_sql_params($params);
137 }
138
139 public function set_source_alias($dbname, $finalelementname) {
140 // Get final element
141 $finalelement = $this->get_final_element($finalelementname);
142 if (!$finalelement) { // Final element incorrect, throw exception
143 throw new base_element_struct_exception('incorrectaliasfinalnamenotfound', $finalelementname);
144 } else {
145 $this->aliases[$dbname] = $finalelement;
146 }
147 }
148
3a1cccc6 149 public function annotate_files($component, $filearea, $elementname, $filesctxid = null) {
64f93798
PS
150 if (!array_key_exists($component, $this->fileannotations)) {
151 $this->fileannotations[$component] = array();
69dd0c8c 152 }
64f93798 153
69dd0c8c 154 if ($elementname !== null) { // Check elementname is valid
64f93798
PS
155 $elementname = $this->find_element($elementname); //TODO: no warning here? (skodak)
156 }
157
158 if (array_key_exists($filearea, $this->fileannotations[$component])) {
159 throw new base_element_struct_exception('annotate_files_duplicate_annotation', "$component/$filearea/$elementname");
69dd0c8c 160 }
64f93798 161
3a1cccc6
EL
162 $info = new stdclass();
163 $info->element = $elementname;
164 $info->contextid = $filesctxid;
165 $this->fileannotations[$component][$filearea] = $info;
69dd0c8c
EL
166 }
167
168 public function annotate_ids($itemname, $elementname) {
169 $element = $this->find_element($elementname);
170 $element->set_annotation_item($itemname);
171 }
172
173 /**
174 * Returns one array containing the element in the
175 * @backup_structure and the areas to be searched
176 */
177 public function get_file_annotations() {
64f93798 178 return $this->fileannotations;
69dd0c8c
EL
179 }
180
181 public function get_source_array() {
182 return $this->var_array;
183 }
184
185 public function get_source_table() {
186 return $this->table;
187 }
188
189 public function get_source_sql() {
190 return $this->sql;
191 }
192
193 public function get_counter() {
194 return $this->counter;
195 }
196
197 /**
198 * Simple filler that, matching by name, will fill both attributes and final elements
199 * depending of this nested element, debugging info about non-matching elements and/or
200 * elements present in both places. Accept both arrays and objects.
201 */
202 public function fill_values($values) {
203 $values = (array)$values;
204
205 foreach ($values as $key => $value) {
206 $found = 0;
207 if ($attribute = $this->get_attribute($key)) { // Set value for attributes
208 $attribute->set_value($value);
209 $found++;
210 }
211 if ($final = $this->get_final_element($key)) { // Set value for final elements
212 $final->set_value($value);
213 $found++;
214 }
215 if (isset($this->aliases[$key])) { // Last chance, set value by processing final element aliases
216 $this->aliases[$key]->set_value($value);
217 $found++;
218 }
219 // Found more than once, notice
220 // TODO: Route this through backup loggers
221 if ($found > 1) {
222 debugging('Key found more than once ' . $key, DEBUG_DEVELOPER);
223 }
224 }
225
226 }
227
228// Protected API starts here
229
230 protected function convert_table_params($params) {
231 return $this->convert_sql_params($params);
232 }
233
234 protected function convert_sql_params($params) {
235 $procparams = array(); // Reset processed params
236 foreach ($params as $key => $param) {
237 $procparams[$key] = $this->find_element($param);
238 }
239 return $procparams;
240 }
241
242 protected function find_element($param) {
243 if ($param == backup::VAR_PARENTID) { // Look for first parent having id attribute/final_element
244 $param = $this->find_first_parent_by_name('id');
245
246 // If the param is array, with key 'sqlparam', return the value without modifications
247 } else if (is_array($param) && isset($param['sqlparam'])) {
248 return $param['sqlparam'];
249
250 } else if (((int)$param) >= 0) { // Search by path if param isn't a backup::XXX candidate
251 $param = $this->find_element_by_path($param);
252 }
253 return $param; // Return the param unmodified
254 }
255
256 /**
257 * Returns one instace of the @base_attribute class to work with
258 * when attributes are added simply by name
259 */
260 protected function get_new_attribute($name) {
261 return new backup_attribute($name);
262 }
263
264 /**
265 * Returns one instace of the @final_element class to work with
266 * when final_elements are added simply by name
267 */
268 protected function get_new_final_element($name) {
269 return new backup_final_element($name);
270 }
271
272 /**
273 * Returns one PHP iterator over each "ocurrence" of this nested
274 * element (array or DB recordset). Delegated to backup_structure_dbops class
275 */
276 protected function get_iterator($processor) {
277 return backup_structure_dbops::get_iterator($this, $this->procparams, $processor);
278 }
279}