MDL-71163 core: support relative dates in activity dates
[moodle.git] / backup / util / structure / backup_nested_element.class.php
blob32abd9ac19dc76409c0837aef544d2d8126a344e
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.
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-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
24 * TODO: Finish phpdocs
27 /**
28 * Instantiable class representing one nestable element (non final) piece of information on backup
30 class backup_nested_element extends base_nested_element implements processable {
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 $tablesortby; // The field to sort by when using the table methods
35 protected $sql; // Raw SQL to fetch records from
36 protected $params; // Unprocessed params as specified in the set_source() call
37 protected $procparams;// Processed (path resolved) params array
38 protected $aliases; // Define DB->final element aliases
39 protected $fileannotations; // array of file areas to be searched by file annotations
40 protected $counter; // Number of instances of this element that have been processed
41 protected $results; // Logs the results we encounter during the process.
42 protected $logs; // Some log messages that could be retrieved later.
44 /**
45 * Constructor - instantiates one backup_nested_element, specifying its basic info.
47 * @param string $name name of the element
48 * @param array $attributes attributes this element will handle (optional, defaults to null)
49 * @param array $final_elements this element will handle (optional, defaults to null)
51 public function __construct($name, $attributes = null, $final_elements = null) {
52 parent::__construct($name, $attributes, $final_elements);
53 $this->var_array = null;
54 $this->table = null;
55 $this->tablesortby = null;
56 $this->sql = null;
57 $this->params = null;
58 $this->procparams= null;
59 $this->aliases = array();
60 $this->fileannotations = array();
61 $this->counter = 0;
62 $this->results = array();
63 $this->logs = array();
66 /**
67 * Process the nested element
69 * @param object $processor the processor
70 * @return void
72 public function process($processor) {
73 if (!$processor instanceof base_processor) { // No correct processor, throw exception
74 throw new base_element_struct_exception('incorrect_processor');
77 $iterator = $this->get_iterator($processor); // Get the iterator over backup-able data
79 foreach ($iterator as $key => $values) { // Process each "ocurrrence" of the nested element (recordset or array)
81 // Fill the values of the attributes and final elements with the $values from the iterator
82 $this->fill_values($values);
84 // Perform pre-process tasks for the nested_element
85 $processor->pre_process_nested_element($this);
87 // Delegate the process of each attribute
88 foreach ($this->get_attributes() as $attribute) {
89 $attribute->process($processor);
92 // Main process tasks for the nested element, once its attributes have been processed
93 $processor->process_nested_element($this);
95 // Delegate the process of each final_element
96 foreach ($this->get_final_elements() as $final_element) {
97 $final_element->process($processor);
100 // Delegate the process to the optigroup
101 if ($this->get_optigroup()) {
102 $this->get_optigroup()->process($processor);
105 // Delegate the process to each child nested_element
106 foreach ($this->get_children() as $child) {
107 $child->process($processor);
110 // Perform post-process tasks for the nested element
111 $processor->post_process_nested_element($this);
113 // Everything processed, clean values before next iteration
114 $this->clean_values();
116 // Increment counter for this element
117 $this->counter++;
119 // For root element, check we only have 1 element
120 if ($this->get_parent() === null && $this->counter > 1) {
121 throw new base_element_struct_exception('root_only_one_ocurrence', $this->get_name());
124 // Close the iterator (DB recordset / array iterator)
125 $iterator->close();
129 * Saves a log message to an array
131 * @see backup_helper::log()
132 * @param string $message to add to the logs
133 * @param int $level level of importance {@link backup::LOG_DEBUG} and other constants
134 * @param mixed $a to be included in $message
135 * @param int $depth of the message
136 * @param display $bool supporting translation via get_string() if true
137 * @return void
139 protected function add_log($message, $level, $a = null, $depth = null, $display = false) {
140 // Adding the result to the oldest parent.
141 if ($this->get_parent()) {
142 $parent = $this->get_grandparent();
143 $parent->add_log($message, $level, $a, $depth, $display);
144 } else {
145 $log = new stdClass();
146 $log->message = $message;
147 $log->level = $level;
148 $log->a = $a;
149 $log->depth = $depth;
150 $log->display = $display;
151 $this->logs[] = $log;
156 * Saves the results to an array
158 * @param array $result associative array
159 * @return void
161 protected function add_result($result) {
162 if (is_array($result)) {
163 // Adding the result to the oldest parent.
164 if ($this->get_parent()) {
165 $parent = $this->get_grandparent();
166 $parent->add_result($result);
167 } else {
168 $this->results = array_merge($this->results, $result);
174 * Returns the logs
176 * @return array of log objects
178 public function get_logs() {
179 return $this->logs;
183 * Returns the results
185 * @return associative array of results
187 public function get_results() {
188 return $this->results;
191 public function set_source_array($arr) {
192 // TODO: Only elements having final elements can set source
193 $this->var_array = $arr;
196 public function set_source_table($table, $params, $sortby = null) {
197 if (!is_array($params)) { // Check we are passing array
198 throw new base_element_struct_exception('setsourcerequiresarrayofparams');
200 // TODO: Only elements having final elements can set source
201 $this->table = $table;
202 $this->procparams = $this->convert_table_params($params);
203 if ($sortby) {
204 $this->tablesortby = $sortby;
208 public function set_source_sql($sql, $params) {
209 if (!is_array($params)) { // Check we are passing array
210 throw new base_element_struct_exception('setsourcerequiresarrayofparams');
212 // TODO: Only elements having final elements can set source
213 $this->sql = $sql;
214 $this->procparams = $this->convert_sql_params($params);
217 public function set_source_alias($dbname, $finalelementname) {
218 // Get final element
219 $finalelement = $this->get_final_element($finalelementname);
220 if (!$finalelement) { // Final element incorrect, throw exception
221 throw new base_element_struct_exception('incorrectaliasfinalnamenotfound', $finalelementname);
222 } else {
223 $this->aliases[$dbname] = $finalelement;
227 public function annotate_files($component, $filearea, $elementname, $filesctxid = null) {
228 if (!array_key_exists($component, $this->fileannotations)) {
229 $this->fileannotations[$component] = array();
232 if ($elementname !== null) { // Check elementname is valid
233 $elementname = $this->find_element($elementname); //TODO: no warning here? (skodak)
236 if (array_key_exists($filearea, $this->fileannotations[$component])) {
237 throw new base_element_struct_exception('annotate_files_duplicate_annotation', "$component/$filearea/$elementname");
240 $info = new stdclass();
241 $info->element = $elementname;
242 $info->contextid = $filesctxid;
243 $this->fileannotations[$component][$filearea] = $info;
246 public function annotate_ids($itemname, $elementname) {
247 $element = $this->find_element($elementname);
248 $element->set_annotation_item($itemname);
252 * Returns one array containing the element in the
253 * @backup_structure and the areas to be searched
255 public function get_file_annotations() {
256 return $this->fileannotations;
259 public function get_source_array() {
260 return $this->var_array;
263 public function get_source_table() {
264 return $this->table;
267 public function get_source_table_sortby() {
268 return $this->tablesortby;
271 public function get_source_sql() {
272 return $this->sql;
275 public function get_counter() {
276 return $this->counter;
280 * Simple filler that, matching by name, will fill both attributes and final elements
281 * depending of this nested element, debugging info about non-matching elements and/or
282 * elements present in both places. Accept both arrays and objects.
284 public function fill_values($values) {
285 $values = (array)$values;
287 foreach ($values as $key => $value) {
288 $found = 0;
289 if ($attribute = $this->get_attribute($key)) { // Set value for attributes
290 $attribute->set_value($value);
291 $found++;
293 if ($final = $this->get_final_element($key)) { // Set value for final elements
294 $final->set_value($value);
295 $found++;
297 if (isset($this->aliases[$key])) { // Last chance, set value by processing final element aliases
298 $this->aliases[$key]->set_value($value);
299 $found++;
301 // Found more than once, notice
302 // TODO: Route this through backup loggers
303 if ($found > 1) {
304 debugging('Key found more than once ' . $key, DEBUG_DEVELOPER);
310 // Protected API starts here
312 protected function convert_table_params($params) {
313 return $this->convert_sql_params($params);
316 protected function convert_sql_params($params) {
317 $procparams = array(); // Reset processed params
318 foreach ($params as $key => $param) {
319 $procparams[$key] = $this->find_element($param);
321 return $procparams;
324 protected function find_element($param) {
325 if ($param == backup::VAR_PARENTID) { // Look for first parent having id attribute/final_element
326 $param = $this->find_first_parent_by_name('id');
328 // If the param is array, with key 'sqlparam', return the value without modifications
329 } else if (is_array($param) && array_key_exists('sqlparam', $param)) {
330 return $param['sqlparam'];
332 } else if (((int)$param) >= 0) { // Search by path if param isn't a backup::XXX candidate
333 $param = $this->find_element_by_path($param);
335 return $param; // Return the param unmodified
339 * Returns one instace of the @base_attribute class to work with
340 * when attributes are added simply by name
342 protected function get_new_attribute($name) {
343 return new backup_attribute($name);
347 * Returns one instace of the @final_element class to work with
348 * when final_elements are added simply by name
350 protected function get_new_final_element($name) {
351 return new backup_final_element($name);
355 * Returns one PHP iterator over each "ocurrence" of this nested
356 * element (array or DB recordset). Delegated to backup_structure_dbops class
358 protected function get_iterator($processor) {
359 return backup_structure_dbops::get_iterator($this, $this->procparams, $processor);