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