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 backup one @backup_structure 27 * 28 * TODO: Finish phpdocs 29 */ 30abstract class backup_structure_step extends backup_step { 31 32 protected $filename; // Name of the file to be generated 33 protected $contenttransformer; // xml content transformer being used 34 // (need it here, apart from xml_writer, 35 // thanks to serialized data to process - 36 // say thanks to blocks!) 37 38 /** 39 * Constructor - instantiates one object of this class 40 */ 41 public function __construct($name, $filename, $task = null) { 42 if (!is_null($task) && !($task instanceof backup_task)) { 43 throw new backup_step_exception('wrong_backup_task_specified'); 44 } 45 $this->filename = $filename; 46 $this->contenttransformer = null; 47 parent::__construct($name, $task); 48 } 49 50 public function execute() { 51 52 if (!$this->execute_condition()) { // Check any condition to execute this 53 return; 54 } 55 56 $fullpath = $this->task->get_taskbasepath(); 57 58 // We MUST have one fullpath here, else, error 59 if (empty($fullpath)) { 60 throw new backup_step_exception('backup_structure_step_undefined_fullpath'); 61 } 62 63 // Append the filename to the fullpath 64 $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; 65 66 // Create output, transformer, writer, processor 67 $xo = new file_xml_output($fullpath); 68 $xt = null; 69 if (class_exists('backup_xml_transformer')) { 70 $xt = new backup_xml_transformer($this->get_courseid()); 71 $this->contenttransformer = $xt; // Save the reference to the transformer 72 // as far as we are going to need it out 73 // from xml_writer (blame serialized data!) 74 } 75 $xw = new xml_writer($xo, $xt); 76 $progress = $this->task->get_progress(); 77 $progress->start_progress($this->get_name()); 78 $pr = new backup_structure_processor($xw, $progress); 79 80 // Set processor variables from settings 81 foreach ($this->get_settings() as $setting) { 82 $pr->set_var($setting->get_name(), $setting->get_value()); 83 } 84 // Add backupid as one more var for processor 85 $pr->set_var(backup::VAR_BACKUPID, $this->get_backupid()); 86 87 // Get structure definition 88 $structure = $this->define_structure(); 89 if (! $structure instanceof backup_nested_element) { 90 throw new backup_step_exception('backup_structure_step_wrong_structure'); 91 } 92 93 // Start writer 94 $xw->start(); 95 96 // Process structure definition 97 $structure->process($pr); 98 99 // Get the results from the nested elements 100 $results = $structure->get_results(); 101 102 // Get the log messages to append to the log 103 $logs = $structure->get_logs(); 104 foreach ($logs as $log) { 105 $this->log($log->message, $log->level, $log->a, $log->depth, $log->display); 106 } 107 108 // Close everything 109 $xw->stop(); 110 $progress->end_progress(); 111 112 // Destroy the structure. It helps PHP 5.2 memory a lot! 113 $structure->destroy(); 114 115 return $results; 116 } 117 118 /** 119 * As far as backup structure steps are implementing backup_plugin stuff, they need to 120 * have the parent task available for wrapping purposes (get course/context....) 121 */ 122 public function get_task() { 123 return $this->task; 124 } 125 126// Protected API starts here 127 128 /** 129 * Add plugin structure to any element in the structure backup tree 130 * 131 * @param string $plugintype type of plugin as defined by core_component::get_plugin_types() 132 * @param backup_nested_element $element element in the structure backup tree that 133 * we are going to add plugin information to 134 * @param bool $multiple to define if multiple plugins can produce information 135 * for each instance of $element (true) or no (false) 136 */ 137 protected function add_plugin_structure($plugintype, $element, $multiple) { 138 139 global $CFG; 140 141 // Check the requested plugintype is a valid one 142 if (!array_key_exists($plugintype, core_component::get_plugin_types($plugintype))) { 143 throw new backup_step_exception('incorrect_plugin_type', $plugintype); 144 } 145 146 // Arrived here, plugin is correct, let's create the optigroup 147 $optigroupname = $plugintype . '_' . $element->get_name() . '_plugin'; 148 $optigroup = new backup_optigroup($optigroupname, null, $multiple); 149 $element->add_child($optigroup); // Add optigroup to stay connected since beginning 150 151 // Get all the optigroup_elements, looking across all the plugin dirs 152 $pluginsdirs = core_component::get_plugin_list($plugintype); 153 foreach ($pluginsdirs as $name => $plugindir) { 154 $classname = 'backup_' . $plugintype . '_' . $name . '_plugin'; 155 $backupfile = $plugindir . '/backup/moodle2/' . $classname . '.class.php'; 156 if (file_exists($backupfile)) { 157 require_once($backupfile); 158 $backupplugin = new $classname($plugintype, $name, $optigroup, $this); 159 // Add plugin returned structure to optigroup 160 $backupplugin->define_plugin_structure($element->get_name()); 161 } 162 } 163 } 164 165 /** 166 * Add subplugin structure for a given plugin to any element in the structure backup tree. 167 * 168 * This method allows the injection of subplugins (of a specified plugin) data to any 169 * element in any backup structure. 170 * 171 * NOTE: Initially subplugins were only available for activities (mod), so only the 172 * {@link backup_activity_structure_step} class had support for them, always 173 * looking for /mod/modulenanme subplugins. This new method is a generalization of the 174 * existing one for activities, supporting all subplugins injecting information everywhere. 175 * 176 * @param string $subplugintype type of subplugin as defined in plugin's db/subplugins.json. 177 * @param backup_nested_element $element element in the backup tree (anywhere) that 178 * we are going to add subplugin information to. 179 * @param bool $multiple to define if multiple subplugins can produce information 180 * for each instance of $element (true) or no (false). 181 * @param string $plugintype type of the plugin. 182 * @param string $pluginname name of the plugin. 183 * @return void 184 */ 185 protected function add_subplugin_structure($subplugintype, $element, $multiple, $plugintype = null, $pluginname = null) { 186 global $CFG; 187 // This global declaration is required, because where we do require_once($backupfile); 188 // That file may in turn try to do require_once($CFG->dirroot ...). 189 // That worked in the past, we should keep it working. 190 191 // Verify if this is a BC call for an activity backup. See NOTE above for this special case. 192 if ($plugintype === null and $pluginname === null) { 193 $plugintype = 'mod'; 194 $pluginname = $this->task->get_modulename(); 195 // TODO: Once all the calls have been changed to add both not null plugintype and pluginname, add a debugging here. 196 } 197 198 // Check the requested plugintype is a valid one. 199 if (!array_key_exists($plugintype, core_component::get_plugin_types())) { 200 throw new backup_step_exception('incorrect_plugin_type', $plugintype); 201 } 202 203 // Check the requested pluginname, for the specified plugintype, is a valid one. 204 if (!array_key_exists($pluginname, core_component::get_plugin_list($plugintype))) { 205 throw new backup_step_exception('incorrect_plugin_name', array($plugintype, $pluginname)); 206 } 207 208 // Check the requested subplugintype is a valid one. 209 $subplugins = core_component::get_subplugins("{$plugintype}_{$pluginname}"); 210 if (null === $subplugins) { 211 throw new backup_step_exception('plugin_missing_subplugins_configuration', [$plugintype, $pluginname]); 212 } 213 if (!array_key_exists($subplugintype, $subplugins)) { 214 throw new backup_step_exception('incorrect_subplugin_type', $subplugintype); 215 } 216 217 // Arrived here, subplugin is correct, let's create the optigroup. 218 $optigroupname = $subplugintype . '_' . $element->get_name() . '_subplugin'; 219 $optigroup = new backup_optigroup($optigroupname, null, $multiple); 220 $element->add_child($optigroup); // Add optigroup to stay connected since beginning. 221 222 // Every subplugin optionally can have a common/parent subplugin 223 // class for shared stuff. 224 $parentclass = 'backup_' . $plugintype . '_' . $pluginname . '_' . $subplugintype . '_subplugin'; 225 $parentfile = core_component::get_component_directory($plugintype . '_' . $pluginname) . 226 '/backup/moodle2/' . $parentclass . '.class.php'; 227 if (file_exists($parentfile)) { 228 require_once($parentfile); 229 } 230 231 // Get all the optigroup_elements, looking over all the subplugin dirs. 232 $subpluginsdirs = core_component::get_plugin_list($subplugintype); 233 foreach ($subpluginsdirs as $name => $subpluginsdir) { 234 $classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin'; 235 $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php'; 236 if (file_exists($backupfile)) { 237 require_once($backupfile); 238 $backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this); 239 // Add subplugin returned structure to optigroup. 240 $backupsubplugin->define_subplugin_structure($element->get_name()); 241 } 242 } 243 } 244 245 /** 246 * To conditionally decide if one step will be executed or no 247 * 248 * For steps needing to be executed conditionally, based in dynamic 249 * conditions (at execution time vs at declaration time) you must 250 * override this function. It will return true if the step must be 251 * executed and false if not 252 */ 253 protected function execute_condition() { 254 return true; 255 } 256 257 /** 258 * Define the structure to be processed by this backup step. 259 * 260 * @return backup_nested_element 261 */ 262 abstract protected function define_structure(); 263} 264