MDL-21432 backup - move all the files handling on restore to use new backup_files_tem...
[moodle.git] / backup / util / dbops / backup_controller_dbops.class.php
CommitLineData
69dd0c8c
EL
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-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 */
24
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 */
33abstract class backup_controller_dbops extends backup_dbops {
34
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();
f60f4666 48 $rec->operation = $controller->get_operation();
69dd0c8c
EL
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 }
73
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 }
86
87 public static function create_backup_ids_temp_table($backupid) {
b8bb45b0
EL
88 self::create_temptable_from_real_table($backupid, 'backup_ids_template', 'backup_ids_temp');
89 }
90
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) {
69dd0c8c
EL
95 global $CFG, $DB;
96 $dbman = $DB->get_manager(); // We are going to use database_manager services
97
b8bb45b0 98 // Note: For now we are going to load the realtablename from core lib/db/install.xml
69dd0c8c
EL
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.
b8bb45b0
EL
102 $templatetablename = $realtablename;
103 $targettablename = $temptablename;
104 $xmlfile = $CFG->dirroot . '/lib/db/install.xml';
105 $xmldb_file = new xmldb_file($xmlfile);
69dd0c8c
EL
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)) {
b8bb45b0 116 throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $xmlfile);
69dd0c8c
EL
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);
125
126 $dbman->create_temp_table($xmldb_table); // And create it
127 }
128
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
132
133 $targettablename = 'backup_ids_temp';
134 $table = new xmldb_table($targettablename);
135 $dbman->drop_temp_table($table); // And drop it
136 }
137
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
145
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 }
158
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) {
164
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());
171
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());
ce937f99 190 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
69dd0c8c
EL
191 }
192 return array($contentinfo, $settingsinfo);
193 }
194
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) {
200
201 $contentinfo = array(
202 'sectionid' => $task->get_sectionid(),
203 'title' => $task->get_name(),
204 'directory' => 'sections/' . 'section_' . $task->get_sectionid());
205
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',
d12fd69b 221 'section' => $prefix,
69dd0c8c
EL
222 'name' => $setting->get_name(),
223 'value' => $setting->get_value());
ce937f99 224 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
69dd0c8c
EL
225 }
226 return array($contentinfo, $settingsinfo);
227 }
228
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) {
234
235 $contentinfo = array(
236 'courseid' => $task->get_courseid(),
237 'title' => $task->get_name(),
238 'directory' => 'course');
239
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());
ce937f99 257 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
69dd0c8c
EL
258 }
259 return array($contentinfo, $settingsinfo);
260 }
261
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) {
267
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());
ce937f99 279 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
69dd0c8c
EL
280 }
281 return array(null, $settingsinfo);
282 }
283
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) {
289
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
294
295 // Details info
cd0034d8 296 $detailsinfo['id'] = $bc->get_id();
69dd0c8c
EL
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();
ce937f99 304 $detailsinfo['userid'] = $bc->get_userid();
cd0034d8 305 $detailsinfo['courseid'] = $bc->get_courseid();
69dd0c8c
EL
306
307
308 // Init content placeholders
309 $contentsinfo['activities'] = array();
310 $contentsinfo['sections'] = array();
311 $contentsinfo['course'] = array();
312
313 // Contents info (extract information from tasks)
314 foreach ($bc->get_plan()->get_tasks() as $task) {
315
316 if ($task instanceof backup_activity_task) { // Activity task
317
318 list($contentinfo, $settings) = self::get_activity_backup_information($task);
319 $contentsinfo['activities'][] = $contentinfo;
320 $settingsinfo = array_merge($settingsinfo, $settings);
321
322 } else if ($task instanceof backup_section_task) { // Section task
323
324 list($contentinfo, $settings) = self::get_section_backup_information($task);
325 $contentsinfo['sections'][] = $contentinfo;
326 $settingsinfo = array_merge($settingsinfo, $settings);
327
328 } else if ($task instanceof backup_course_task) { // Course task
329
330 list($contentinfo, $settings) = self::get_course_backup_information($task);
331 $contentsinfo['course'][] = $contentinfo;
332 $settingsinfo = array_merge($settingsinfo, $settings);
333
334 } else if ($task instanceof backup_root_task) { // Root task
335
336 list($contentinfo, $settings) = self::get_root_backup_information($task);
337 $settingsinfo = array_merge($settingsinfo, $settings);
338 }
339 }
340
341 return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo);
342 }
343
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;
350
351 if ($CFG->backup_version < backup::VERSION) {
352 set_config('backup_version', backup::VERSION);
353 set_config('backup_release', backup::RELEASE);
354 }
355 }
61ebef8f
SH
356
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 }
69dd0c8c 392}