8a275cb20794c54312bfb96971df43026ff935d8
[moodle.git] / backup / util / dbops / backup_controller_dbops.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-dbops
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  * Non instantiable helper class providing DB support to the @backup_controller
27  *
28  * This class contains various static methods available for all the DB operations
29  * performed by the backup_controller class
30  *
31  * TODO: Finish phpdocs
32  */
33 abstract class backup_controller_dbops extends backup_dbops {
35     public static function save_controller($controller, $checksum) {
36         global $DB;
37         // Check we are going to save one backup_controller
38         if (! $controller instanceof backup_controller) {
39             throw new backup_controller_exception('backup_controller_expected');
40         }
41         // Check checksum is ok. Sounds silly but it isn't ;-)
42         if (!$controller->is_checksum_correct($checksum)) {
43             throw new backup_dbops_exception('backup_controller_dbops_saving_checksum_mismatch');
44         }
45         // Get all the columns
46         $rec = new stdclass();
47         $rec->backupid     = $controller->get_backupid();
48         $rec->operation    = $controller->get_operation();
49         $rec->type         = $controller->get_type();
50         $rec->itemid       = $controller->get_id();
51         $rec->format       = $controller->get_format();
52         $rec->interactive  = $controller->get_interactive();
53         $rec->purpose      = $controller->get_mode();
54         $rec->userid       = $controller->get_userid();
55         $rec->status       = $controller->get_status();
56         $rec->execution    = $controller->get_execution();
57         $rec->executiontime= $controller->get_executiontime();
58         $rec->checksum     = $checksum;
59         // Serialize information
60         $rec->controller = base64_encode(serialize($controller));
61         // Send it to DB
62         if ($recexists = $DB->get_record('backup_controllers', array('backupid' => $rec->backupid))) {
63             $rec->id = $recexists->id;
64             $rec->timemodified = time();
65             $DB->update_record('backup_controllers', $rec);
66         } else {
67             $rec->timecreated = time();
68             $rec->timemodified = 0;
69             $rec->id = $DB->insert_record('backup_controllers', $rec);
70         }
71         return $rec->id;
72     }
74     public static function load_controller($backupid) {
75         global $DB;
76         if (! $controllerrec = $DB->get_record('backup_controllers', array('backupid' => $backupid))) {
77             throw new backup_dbops_exception('backup_controller_dbops_nonexisting');
78         }
79         $controller = unserialize(base64_decode($controllerrec->controller));
80         // Check checksum is ok. Sounds silly but it isn't ;-)
81         if (!$controller->is_checksum_correct($controllerrec->checksum)) {
82             throw new backup_dbops_exception('backup_controller_dbops_loading_checksum_mismatch');
83         }
84         return $controller;
85     }
87     public static function create_backup_ids_temp_table($backupid) {
88         global $CFG, $DB;
89         $dbman = $DB->get_manager(); // We are going to use database_manager services
91         // Note: For now we are going to load the backup_ids_template from core lib/db/install.xml
92         // that way, any change in the "template" will be applied here automatically. If this causes
93         // too much slow, we can always forget about the template and keep maintained the xmldb_table
94         // structure inline - manually - here.
95         $templatetablename = 'backup_ids_template';
96         $targettablename = 'backup_ids_temp';
97         $xmldb_file = new xmldb_file($CFG->dirroot . '/lib/db/install.xml');
98         if (!$xmldb_file->fileExists()) {
99             throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist');
100         }
101         $loaded = $xmldb_file->loadXMLStructure();
102         if (!$loaded || !$xmldb_file->isLoaded()) {
103             throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??');
104         }
105         $xmldb_structure = $xmldb_file->getStructure();
106         $xmldb_table = $xmldb_structure->getTable($templatetablename);
107         if (is_null($xmldb_table)) {
108             throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $file);
109         }
110         // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
111         $xmldb_table->getField('backupid')->setDefault($backupid);
112         // Clean prev & next, we are alone
113         $xmldb_table->setNext(null);
114         $xmldb_table->setPrevious(null);
115         // Rename
116         $xmldb_table->setName($targettablename);
118         $dbman->create_temp_table($xmldb_table); // And create it
119     }
121     public static function drop_backup_ids_temp_table($backupid) {
122         global $DB;
123         $dbman = $DB->get_manager(); // We are going to use database_manager services
125         $targettablename = 'backup_ids_temp';
126         $table = new xmldb_table($targettablename);
127         $dbman->drop_temp_table($table); // And drop it
128     }
130     /**
131      * Given one type and id from controller, return the corresponding courseid
132      */
133     public static function get_courseid_from_type_id($type, $id) {
134         global $DB;
135         if ($type == backup::TYPE_1COURSE) {
136             return $id; // id is the course id
138         } else if ($type == backup::TYPE_1SECTION) {
139             if (! $courseid = $DB->get_field('course_sections', 'course', array('id' => $id))) {
140                 throw new backup_dbops_exception('course_not_found_for_section', $id);
141             }
142             return $courseid;
143         } else if ($type == backup::TYPE_1ACTIVITY) {
144             if (! $courseid = $DB->get_field('course_modules', 'course', array('id' => $id))) {
145                 throw new backup_dbops_exception('course_not_found_for_moduleid', $id);
146             }
147             return $courseid;
148         }
149     }
151     /**
152      * Given one activity task, return the activity information and related settings
153      * Used by get_moodle_backup_information()
154      */
155     private static function get_activity_backup_information($task) {
157         $contentinfo = array(
158             'moduleid'   => $task->get_moduleid(),
159             'sectionid'  => $task->get_sectionid(),
160             'modulename' => $task->get_modulename(),
161             'title'      => $task->get_name(),
162             'directory'  => 'activities/' . $task->get_modulename() . '_' . $task->get_moduleid());
164         // Now get activity settings
165         // Calculate prefix to find valid settings
166         $prefix = basename($contentinfo['directory']);
167         $settingsinfo = array();
168         foreach ($task->get_settings() as $setting) {
169             // Discard ones without valid prefix
170             if (strpos($setting->get_name(), $prefix) !== 0) {
171                 continue;
172             }
173             // Validate level is correct (activity)
174             if ($setting->get_level() != backup_setting::ACTIVITY_LEVEL) {
175                 throw new backup_controller_exception('setting_not_activity_level', $setting);
176             }
177             $settinginfo = array(
178                 'level'    => 'activity',
179                 'activity' => $prefix,
180                 'name'     => $setting->get_name(),
181                 'value'    => $setting->get_value());
182             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
183         }
184         return array($contentinfo, $settingsinfo);
185     }
187     /**
188      * Given one section task, return the section information and related settings
189      * Used by get_moodle_backup_information()
190      */
191     private static function get_section_backup_information($task) {
193         $contentinfo = array(
194             'sectionid'  => $task->get_sectionid(),
195             'title'      => $task->get_name(),
196             'directory'  => 'sections/' . 'section_' . $task->get_sectionid());
198         // Now get section settings
199         // Calculate prefix to find valid settings
200         $prefix = basename($contentinfo['directory']);
201         $settingsinfo = array();
202         foreach ($task->get_settings() as $setting) {
203             // Discard ones without valid prefix
204             if (strpos($setting->get_name(), $prefix) !== 0) {
205                 continue;
206             }
207             // Validate level is correct (section)
208             if ($setting->get_level() != backup_setting::SECTION_LEVEL) {
209                 throw new backup_controller_exception('setting_not_section_level', $setting);
210             }
211             $settinginfo = array(
212                 'level'    => 'section',
213                 'section'  => $prefix,
214                 'name'     => $setting->get_name(),
215                 'value'    => $setting->get_value());
216             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
217         }
218         return array($contentinfo, $settingsinfo);
219     }
221     /**
222      * Given one course task, return the course information and related settings
223      * Used by get_moodle_backup_information()
224      */
225     private static function get_course_backup_information($task) {
227         $contentinfo = array(
228             'courseid'   => $task->get_courseid(),
229             'title'      => $task->get_name(),
230             'directory'  => 'course');
232         // Now get course settings
233         // Calculate prefix to find valid settings
234         $prefix = basename($contentinfo['directory']);
235         $settingsinfo = array();
236         foreach ($task->get_settings() as $setting) {
237             // Discard ones without valid prefix
238             if (strpos($setting->get_name(), $prefix) !== 0) {
239                 continue;
240             }
241             // Validate level is correct (course)
242             if ($setting->get_level() != backup_setting::COURSE_LEVEL) {
243                 throw new backup_controller_exception('setting_not_course_level', $setting);
244             }
245             $settinginfo = array(
246                 'level'    => 'course',
247                 'name'     => $setting->get_name(),
248                 'value'    => $setting->get_value());
249             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
250         }
251         return array($contentinfo, $settingsinfo);
252     }
254     /**
255      * Given one root task, return the course information and related settings
256      * Used by get_moodle_backup_information()
257      */
258     private static function get_root_backup_information($task) {
260         // Now get root settings
261         $settingsinfo = array();
262         foreach ($task->get_settings() as $setting) {
263             // Validate level is correct (root)
264             if ($setting->get_level() != backup_setting::ROOT_LEVEL) {
265                 throw new backup_controller_exception('setting_not_root_level', $setting);
266             }
267             $settinginfo = array(
268                 'level'    => 'root',
269                 'name'     => $setting->get_name(),
270                 'value'    => $setting->get_value());
271             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
272         }
273         return array(null, $settingsinfo);
274     }
276     /**
277      * Get details information for main moodle_backup.xml file, extracting it from
278      * the specified controller
279      */
280     public static function get_moodle_backup_information($backupid) {
282         $detailsinfo = array(); // Information details
283         $contentsinfo= array(); // Information about backup contents
284         $settingsinfo= array(); // Information about backup settings
285         $bc = self::load_controller($backupid); // Load controller
287         // Details info
288         $detailsinfo['id'] = $bc->get_id();
289         $detailsinfo['backup_id'] = $bc->get_backupid();
290         $detailsinfo['type'] = $bc->get_type();
291         $detailsinfo['format'] = $bc->get_format();
292         $detailsinfo['interactive'] = $bc->get_interactive();
293         $detailsinfo['mode'] = $bc->get_mode();
294         $detailsinfo['execution'] = $bc->get_execution();
295         $detailsinfo['executiontime'] = $bc->get_executiontime();
296         $detailsinfo['userid'] = $bc->get_userid();
297         $detailsinfo['courseid'] = $bc->get_courseid();
300         // Init content placeholders
301         $contentsinfo['activities'] = array();
302         $contentsinfo['sections']   = array();
303         $contentsinfo['course']     = array();
305         // Contents info (extract information from tasks)
306         foreach ($bc->get_plan()->get_tasks() as $task) {
308             if ($task instanceof backup_activity_task) { // Activity task
310                 list($contentinfo, $settings) = self::get_activity_backup_information($task);
311                 $contentsinfo['activities'][] = $contentinfo;
312                 $settingsinfo = array_merge($settingsinfo, $settings);
314             } else if ($task instanceof backup_section_task) { // Section task
316                 list($contentinfo, $settings) = self::get_section_backup_information($task);
317                 $contentsinfo['sections'][] = $contentinfo;
318                 $settingsinfo = array_merge($settingsinfo, $settings);
320             } else if ($task instanceof backup_course_task) { // Course task
322                 list($contentinfo, $settings) = self::get_course_backup_information($task);
323                 $contentsinfo['course'][] = $contentinfo;
324                 $settingsinfo = array_merge($settingsinfo, $settings);
326             } else if ($task instanceof backup_root_task) { // Root task
328                 list($contentinfo, $settings) = self::get_root_backup_information($task);
329                 $settingsinfo = array_merge($settingsinfo, $settings);
330             }
331         }
333         return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo);
334     }
336     /**
337      * Update CFG->backup_version and CFG->backup_release if change in
338      * version is detected.
339      */
340     public static function apply_version_and_release() {
341         global $CFG;
343         if ($CFG->backup_version < backup::VERSION) {
344             set_config('backup_version', backup::VERSION);
345             set_config('backup_release', backup::RELEASE);
346         }
347     }
349     /**
350      * Sets the controller settings default values from the backup config.
351      * 
352      * @param backup_controller $controller
353      */
354     public static function apply_general_config_defaults(backup_controller $controller) {
355         $settings = array(
356             // Config name                      => Setting name
357             'backup_general_users'              => 'users',
358             'backup_general_anonymize'          => 'anonymize',
359             'backup_general_role_assignments'   => 'role_assignments',
360             'backup_general_user_files'         => 'user_files',
361             'backup_general_activities'         => 'activities',
362             'backup_general_blocks'             => 'blocks',
363             'backup_general_filters'            => 'filters',
364             'backup_general_comments'           => 'comments',
365             'backup_general_userscompletion'    => 'userscompletion',
366             'backup_general_logs'               => 'logs',
367             'backup_general_histories'          => 'grade_histories'
368         );
369         $plan = $controller->get_plan();
370         foreach ($settings as $config=>$settingname) {
371             $value = get_config('backup', $config);
372             $locked = (get_config('backup', $config.'_locked') == true);
373             if ($plan->setting_exists($settingname)) {
374                 $setting = $plan->get_setting($settingname);
375                 if ($setting->get_value() != $value || 1==1) {
376                     $setting->set_value($value);
377                     if ($locked) {
378                         $setting->set_status(base_setting::LOCKED_BY_CONFIG);
379                     }
380                 }
381             }
382         }
383     }