MDL-52523 backup of subplugins: add missing global
[moodle.git] / backup / util / plan / backup_structure_step.class.php
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.
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/>.
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  */
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 {
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!)
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     }
50     public function execute() {
52         if (!$this->execute_condition()) { // Check any condition to execute this
53             return;
54         }
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');
61         }
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);
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);
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());
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         }
93         // Start writer
94         $xw->start();
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);
106         }
108         // Close everything
109         $xw->stop();
110         $progress->end_progress();
112         // Destroy the structure. It helps PHP 5.2 memory a lot!
113         $structure->destroy();
115         return $results;
116     }
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     }
126 // Protected API starts here
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) {
139         global $CFG;
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         }
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());
161             }
162         }
163     }
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.php.
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.
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         }
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         }
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         }
208         // Check the requested subplugintype is a valid one.
209         $subpluginsfile = core_component::get_component_directory($plugintype . '_' . $pluginname) . '/db/subplugins.php';
210         if (!file_exists($subpluginsfile)) {
211              throw new backup_step_exception('plugin_missing_subplugins_php_file', array($plugintype, $pluginname));
212         }
213         include($subpluginsfile);
214         if (!array_key_exists($subplugintype, $subplugins)) {
215              throw new backup_step_exception('incorrect_subplugin_type', $subplugintype);
216         }
218         // Arrived here, subplugin is correct, let's create the optigroup.
219         $optigroupname = $subplugintype . '_' . $element->get_name() . '_subplugin';
220         $optigroup = new backup_optigroup($optigroupname, null, $multiple);
221         $element->add_child($optigroup); // Add optigroup to stay connected since beginning.
223         // Every subplugin optionally can have a common/parent subplugin
224         // class for shared stuff.
225         $parentclass = 'backup_' . $plugintype . '_' . $pluginname . '_' . $subplugintype . '_subplugin';
226         $parentfile = core_component::get_component_directory($plugintype . '_' . $pluginname) .
227             '/backup/moodle2/' . $parentclass . '.class.php';
228         if (file_exists($parentfile)) {
229             require_once($parentfile);
230         }
232         // Get all the optigroup_elements, looking over all the subplugin dirs.
233         $subpluginsdirs = core_component::get_plugin_list($subplugintype);
234         foreach ($subpluginsdirs as $name => $subpluginsdir) {
235             $classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin';
236             $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
237             if (file_exists($backupfile)) {
238                 require_once($backupfile);
239                 $backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this);
240                 // Add subplugin returned structure to optigroup.
241                 $backupsubplugin->define_subplugin_structure($element->get_name());
242             }
243         }
244     }
246     /**
247      * To conditionally decide if one step will be executed or no
248      *
249      * For steps needing to be executed conditionally, based in dynamic
250      * conditions (at execution time vs at declaration time) you must
251      * override this function. It will return true if the step must be
252      * executed and false if not
253      */
254     protected function execute_condition() {
255         return true;
256     }
258     /**
259      * Define the structure to be processed by this backup step.
260      *
261      * @return backup_nested_element
262      */
263     abstract protected function define_structure();