db4688b08cfe2d5d7c111aa3b8c1f4caf299b9b0
[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         self::create_temptable_from_real_table($backupid, 'backup_ids_template', 'backup_ids_temp');
89     }
91     /**
92      * Given one "real" tablename, create one temp table suitable for be used in backup/restore operations
93      */
94     public static function create_temptable_from_real_table($backupid, $realtablename, $temptablename) {
95         global $CFG, $DB;
96         $dbman = $DB->get_manager(); // We are going to use database_manager services
98         // Note: For now we are going to load the realtablename from core lib/db/install.xml
99         // that way, any change in the "template" will be applied here automatically. If this causes
100         // too much slow, we can always forget about the template and keep maintained the xmldb_table
101         // structure inline - manually - here.
102         $templatetablename = $realtablename;
103         $targettablename   = $temptablename;
104         $xmlfile = $CFG->dirroot . '/lib/db/install.xml';
105         $xmldb_file = new xmldb_file($xmlfile);
106         if (!$xmldb_file->fileExists()) {
107             throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist');
108         }
109         $loaded = $xmldb_file->loadXMLStructure();
110         if (!$loaded || !$xmldb_file->isLoaded()) {
111             throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??');
112         }
113         $xmldb_structure = $xmldb_file->getStructure();
114         $xmldb_table = $xmldb_structure->getTable($templatetablename);
115         if (is_null($xmldb_table)) {
116             throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $xmlfile);
117         }
118         // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
119         $xmldb_table->getField('backupid')->setDefault($backupid);
120         // Clean prev & next, we are alone
121         $xmldb_table->setNext(null);
122         $xmldb_table->setPrevious(null);
123         // Rename
124         $xmldb_table->setName($targettablename);
126         $dbman->create_temp_table($xmldb_table); // And create it
127     }
129     public static function drop_backup_ids_temp_table($backupid) {
130         global $DB;
131         $dbman = $DB->get_manager(); // We are going to use database_manager services
133         $targettablename = 'backup_ids_temp';
134         $table = new xmldb_table($targettablename);
135         $dbman->drop_temp_table($table); // And drop it
136     }
138     /**
139      * Given one type and id from controller, return the corresponding courseid
140      */
141     public static function get_courseid_from_type_id($type, $id) {
142         global $DB;
143         if ($type == backup::TYPE_1COURSE) {
144             return $id; // id is the course id
146         } else if ($type == backup::TYPE_1SECTION) {
147             if (! $courseid = $DB->get_field('course_sections', 'course', array('id' => $id))) {
148                 throw new backup_dbops_exception('course_not_found_for_section', $id);
149             }
150             return $courseid;
151         } else if ($type == backup::TYPE_1ACTIVITY) {
152             if (! $courseid = $DB->get_field('course_modules', 'course', array('id' => $id))) {
153                 throw new backup_dbops_exception('course_not_found_for_moduleid', $id);
154             }
155             return $courseid;
156         }
157     }
159     /**
160      * Given one activity task, return the activity information and related settings
161      * Used by get_moodle_backup_information()
162      */
163     private static function get_activity_backup_information($task) {
165         $contentinfo = array(
166             'moduleid'   => $task->get_moduleid(),
167             'sectionid'  => $task->get_sectionid(),
168             'modulename' => $task->get_modulename(),
169             'title'      => $task->get_name(),
170             'directory'  => 'activities/' . $task->get_modulename() . '_' . $task->get_moduleid());
172         // Now get activity settings
173         // Calculate prefix to find valid settings
174         $prefix = basename($contentinfo['directory']);
175         $settingsinfo = array();
176         foreach ($task->get_settings() as $setting) {
177             // Discard ones without valid prefix
178             if (strpos($setting->get_name(), $prefix) !== 0) {
179                 continue;
180             }
181             // Validate level is correct (activity)
182             if ($setting->get_level() != backup_setting::ACTIVITY_LEVEL) {
183                 throw new backup_controller_exception('setting_not_activity_level', $setting);
184             }
185             $settinginfo = array(
186                 'level'    => 'activity',
187                 'activity' => $prefix,
188                 'name'     => $setting->get_name(),
189                 'value'    => $setting->get_value());
190             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
191         }
192         return array($contentinfo, $settingsinfo);
193     }
195     /**
196      * Given one section task, return the section information and related settings
197      * Used by get_moodle_backup_information()
198      */
199     private static function get_section_backup_information($task) {
201         $contentinfo = array(
202             'sectionid'  => $task->get_sectionid(),
203             'title'      => $task->get_name(),
204             'directory'  => 'sections/' . 'section_' . $task->get_sectionid());
206         // Now get section settings
207         // Calculate prefix to find valid settings
208         $prefix = basename($contentinfo['directory']);
209         $settingsinfo = array();
210         foreach ($task->get_settings() as $setting) {
211             // Discard ones without valid prefix
212             if (strpos($setting->get_name(), $prefix) !== 0) {
213                 continue;
214             }
215             // Validate level is correct (section)
216             if ($setting->get_level() != backup_setting::SECTION_LEVEL) {
217                 throw new backup_controller_exception('setting_not_section_level', $setting);
218             }
219             $settinginfo = array(
220                 'level'    => 'section',
221                 'section'  => $prefix,
222                 'name'     => $setting->get_name(),
223                 'value'    => $setting->get_value());
224             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
225         }
226         return array($contentinfo, $settingsinfo);
227     }
229     /**
230      * Given one course task, return the course information and related settings
231      * Used by get_moodle_backup_information()
232      */
233     private static function get_course_backup_information($task) {
235         $contentinfo = array(
236             'courseid'   => $task->get_courseid(),
237             'title'      => $task->get_name(),
238             'directory'  => 'course');
240         // Now get course settings
241         // Calculate prefix to find valid settings
242         $prefix = basename($contentinfo['directory']);
243         $settingsinfo = array();
244         foreach ($task->get_settings() as $setting) {
245             // Discard ones without valid prefix
246             if (strpos($setting->get_name(), $prefix) !== 0) {
247                 continue;
248             }
249             // Validate level is correct (course)
250             if ($setting->get_level() != backup_setting::COURSE_LEVEL) {
251                 throw new backup_controller_exception('setting_not_course_level', $setting);
252             }
253             $settinginfo = array(
254                 'level'    => 'course',
255                 'name'     => $setting->get_name(),
256                 'value'    => $setting->get_value());
257             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
258         }
259         return array($contentinfo, $settingsinfo);
260     }
262     /**
263      * Given one root task, return the course information and related settings
264      * Used by get_moodle_backup_information()
265      */
266     private static function get_root_backup_information($task) {
268         // Now get root settings
269         $settingsinfo = array();
270         foreach ($task->get_settings() as $setting) {
271             // Validate level is correct (root)
272             if ($setting->get_level() != backup_setting::ROOT_LEVEL) {
273                 throw new backup_controller_exception('setting_not_root_level', $setting);
274             }
275             $settinginfo = array(
276                 'level'    => 'root',
277                 'name'     => $setting->get_name(),
278                 'value'    => $setting->get_value());
279             $settingsinfo[$setting->get_name()] = (object)$settinginfo;
280         }
281         return array(null, $settingsinfo);
282     }
284     /**
285      * Get details information for main moodle_backup.xml file, extracting it from
286      * the specified controller
287      */
288     public static function get_moodle_backup_information($backupid) {
290         $detailsinfo = array(); // Information details
291         $contentsinfo= array(); // Information about backup contents
292         $settingsinfo= array(); // Information about backup settings
293         $bc = self::load_controller($backupid); // Load controller
295         // Details info
296         $detailsinfo['id'] = $bc->get_id();
297         $detailsinfo['backup_id'] = $bc->get_backupid();
298         $detailsinfo['type'] = $bc->get_type();
299         $detailsinfo['format'] = $bc->get_format();
300         $detailsinfo['interactive'] = $bc->get_interactive();
301         $detailsinfo['mode'] = $bc->get_mode();
302         $detailsinfo['execution'] = $bc->get_execution();
303         $detailsinfo['executiontime'] = $bc->get_executiontime();
304         $detailsinfo['userid'] = $bc->get_userid();
305         $detailsinfo['courseid'] = $bc->get_courseid();
308         // Init content placeholders
309         $contentsinfo['activities'] = array();
310         $contentsinfo['sections']   = array();
311         $contentsinfo['course']     = array();
313         // Contents info (extract information from tasks)
314         foreach ($bc->get_plan()->get_tasks() as $task) {
316             if ($task instanceof backup_activity_task) { // Activity task
318                 list($contentinfo, $settings) = self::get_activity_backup_information($task);
319                 $contentsinfo['activities'][] = $contentinfo;
320                 $settingsinfo = array_merge($settingsinfo, $settings);
322             } else if ($task instanceof backup_section_task) { // Section task
324                 list($contentinfo, $settings) = self::get_section_backup_information($task);
325                 $contentsinfo['sections'][] = $contentinfo;
326                 $settingsinfo = array_merge($settingsinfo, $settings);
328             } else if ($task instanceof backup_course_task) { // Course task
330                 list($contentinfo, $settings) = self::get_course_backup_information($task);
331                 $contentsinfo['course'][] = $contentinfo;
332                 $settingsinfo = array_merge($settingsinfo, $settings);
334             } else if ($task instanceof backup_root_task) { // Root task
336                 list($contentinfo, $settings) = self::get_root_backup_information($task);
337                 $settingsinfo = array_merge($settingsinfo, $settings);
338             }
339         }
341         return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo);
342     }
344     /**
345      * Update CFG->backup_version and CFG->backup_release if change in
346      * version is detected.
347      */
348     public static function apply_version_and_release() {
349         global $CFG;
351         if ($CFG->backup_version < backup::VERSION) {
352             set_config('backup_version', backup::VERSION);
353             set_config('backup_release', backup::RELEASE);
354         }
355     }
357     /**
358      * Sets the controller settings default values from the backup config.
359      * 
360      * @param backup_controller $controller
361      */
362     public static function apply_general_config_defaults(backup_controller $controller) {
363         $settings = array(
364             // Config name                      => Setting name
365             'backup_general_users'              => 'users',
366             'backup_general_anonymize'          => 'anonymize',
367             'backup_general_role_assignments'   => 'role_assignments',
368             'backup_general_user_files'         => 'user_files',
369             'backup_general_activities'         => 'activities',
370             'backup_general_blocks'             => 'blocks',
371             'backup_general_filters'            => 'filters',
372             'backup_general_comments'           => 'comments',
373             'backup_general_userscompletion'    => 'userscompletion',
374             'backup_general_logs'               => 'logs',
375             'backup_general_histories'          => 'grade_histories'
376         );
377         $plan = $controller->get_plan();
378         foreach ($settings as $config=>$settingname) {
379             $value = get_config('backup', $config);
380             $locked = (get_config('backup', $config.'_locked') == true);
381             if ($plan->setting_exists($settingname)) {
382                 $setting = $plan->get_setting($settingname);
383                 if ($setting->get_value() != $value || 1==1) {
384                     $setting->set_value($value);
385                     if ($locked) {
386                         $setting->set_status(base_setting::LOCKED_BY_CONFIG);
387                     }
388                 }
389             }
390         }
391     }