3 // This file is part of Moodle - http://moodle.org/
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.
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/>.
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
26 * Abstract class defining the needed stuff to backup one @backup_structure
28 * TODO: Finish phpdocs
30 abstract class backup_structure_step
extends backup_step
{
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!)
39 * Constructor - instantiates one object of this class
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');
45 $this->filename
= $filename;
46 $this->contenttransformer
= null;
47 parent
::__construct($name, $task);
50 public function execute() {
52 if (!$this->execute_condition()) { // Check any condition to execute this
56 $fullpath = $this->task
->get_taskbasepath();
58 // We MUST have one fullpath here, else, error
59 if (empty($fullpath)) {
60 throw new backup_step_exception('backup_structure_step_undefined_fullpath');
63 // Append the filename to the fullpath
64 $fullpath = rtrim($fullpath, '/') . '/' . $this->filename
;
66 // Create output, transformer, writer, processor
67 $xo = new file_xml_output($fullpath);
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!)
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);
80 // Set processor variables from settings
81 foreach ($this->get_settings() as $setting) {
82 $pr->set_var($setting->get_name(), $setting->get_value());
84 // Add backupid as one more var for processor
85 $pr->set_var(backup
::VAR_BACKUPID
, $this->get_backupid());
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');
96 // Process structure definition
97 $structure->process($pr);
99 // Get the results from the nested elements
100 $results = $structure->get_results();
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
);
110 $progress->end_progress();
112 // Destroy the structure. It helps PHP 5.2 memory a lot!
113 $structure->destroy();
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....)
122 public function get_task() {
126 // Protected API starts here
129 * Add plugin structure to any element in the structure backup tree
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)
137 protected function add_plugin_structure($plugintype, $element, $multiple) {
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);
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
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());
166 * Add subplugin structure for a given plugin to any element in the structure backup tree.
168 * This method allows the injection of subplugins (of a specified plugin) data to any
169 * element in any backup structure.
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.
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.
185 protected function add_subplugin_structure($subplugintype, $element, $multiple, $plugintype = null, $pluginname = null) {
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.
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) {
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.
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);
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));
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]);
213 if (!array_key_exists($subplugintype, $subplugins)) {
214 throw new backup_step_exception('incorrect_subplugin_type', $subplugintype);
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.
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);
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());
246 * To conditionally decide if one step will be executed or no
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
253 protected function execute_condition() {
258 * Define the structure to be processed by this backup step.
260 * @return backup_nested_element
262 abstract protected function define_structure();