weekly release 3.8.5+
[moodle.git] / backup / util / dbops / backup_controller_dbops.class.php
blobce9c1ba923bebce38a3aed7bff59c6893ca577c7
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.
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
25 /**
26 * Non instantiable helper class providing DB support to the @backup_controller
28 * This class contains various static methods available for all the DB operations
29 * performed by the backup_controller class
31 * TODO: Finish phpdocs
33 abstract class backup_controller_dbops extends backup_dbops {
35 /**
36 * @var string Backup id for cached backup_includes_files result.
38 protected static $includesfilescachebackupid;
40 /**
41 * @var int Cached backup_includes_files result
43 protected static $includesfilescache;
45 /**
46 * Send one backup controller to DB
48 * @param backup_controller $controller controller to send to DB
49 * @param string $checksum hash of the controller to be checked
50 * @param bool $includeobj to decide if the object itself must be updated (true) or no (false)
51 * @param bool $cleanobj to decide if the object itself must be cleaned (true) or no (false)
52 * @return int id of the controller record in the DB
53 * @throws backup_controller_exception|backup_dbops_exception
55 public static function save_controller($controller, $checksum, $includeobj = true, $cleanobj = false) {
56 global $DB;
57 // Check we are going to save one backup_controller
58 if (! $controller instanceof backup_controller) {
59 throw new backup_controller_exception('backup_controller_expected');
61 // Check checksum is ok. Only if we are including object info. Sounds silly but it isn't ;-).
62 if ($includeobj and !$controller->is_checksum_correct($checksum)) {
63 throw new backup_dbops_exception('backup_controller_dbops_saving_checksum_mismatch');
65 // Cannot request to $includeobj and $cleanobj at the same time.
66 if ($includeobj and $cleanobj) {
67 throw new backup_dbops_exception('backup_controller_dbops_saving_cannot_include_and_delete');
69 // Get all the columns
70 $rec = new stdclass();
71 $rec->backupid = $controller->get_backupid();
72 $rec->operation = $controller->get_operation();
73 $rec->type = $controller->get_type();
74 $rec->itemid = $controller->get_id();
75 $rec->format = $controller->get_format();
76 $rec->interactive = $controller->get_interactive();
77 $rec->purpose = $controller->get_mode();
78 $rec->userid = $controller->get_userid();
79 $rec->status = $controller->get_status();
80 $rec->execution = $controller->get_execution();
81 $rec->executiontime= $controller->get_executiontime();
82 $rec->checksum = $checksum;
83 // Serialize information
84 if ($includeobj) {
85 $rec->controller = base64_encode(serialize($controller));
86 } else if ($cleanobj) {
87 $rec->controller = '';
89 // Send it to DB
90 if ($recexists = $DB->get_record('backup_controllers', array('backupid' => $rec->backupid))) {
91 $rec->id = $recexists->id;
92 $rec->timemodified = time();
93 $DB->update_record('backup_controllers', $rec);
94 } else {
95 $rec->timecreated = time();
96 $rec->timemodified = 0;
97 $rec->id = $DB->insert_record('backup_controllers', $rec);
99 return $rec->id;
102 public static function load_controller($backupid) {
103 global $DB;
104 if (! $controllerrec = $DB->get_record('backup_controllers', array('backupid' => $backupid))) {
105 throw new backup_dbops_exception('backup_controller_dbops_nonexisting');
107 $controller = unserialize(base64_decode($controllerrec->controller));
108 if (!is_object($controller)) {
109 // The controller field of the table did not contain a serialized object.
110 // It is made empty after it has been used successfully, it is likely that
111 // the user has pressed the browser back button at some point.
112 throw new backup_dbops_exception('backup_controller_dbops_loading_invalid_controller');
114 // Check checksum is ok. Sounds silly but it isn't ;-)
115 if (!$controller->is_checksum_correct($controllerrec->checksum)) {
116 throw new backup_dbops_exception('backup_controller_dbops_loading_checksum_mismatch');
118 return $controller;
121 public static function create_backup_ids_temp_table($backupid) {
122 global $CFG, $DB;
123 $dbman = $DB->get_manager(); // We are going to use database_manager services
125 $xmldb_table = new xmldb_table('backup_ids_temp');
126 $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
127 // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
128 $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
129 $xmldb_table->add_field('itemname', XMLDB_TYPE_CHAR, 160, null, XMLDB_NOTNULL, null, null);
130 $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
131 $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
132 $xmldb_table->add_field('parentitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
133 $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
134 $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
135 $xmldb_table->add_key('backupid_itemname_itemid_uk', XMLDB_KEY_UNIQUE, array('backupid','itemname','itemid'));
136 $xmldb_table->add_index('backupid_parentitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','parentitemid'));
137 $xmldb_table->add_index('backupid_itemname_newitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','newitemid'));
139 $dbman->create_temp_table($xmldb_table); // And create it
143 public static function create_backup_files_temp_table($backupid) {
144 global $CFG, $DB;
145 $dbman = $DB->get_manager(); // We are going to use database_manager services
147 $xmldb_table = new xmldb_table('backup_files_temp');
148 $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
149 // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
150 $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
151 $xmldb_table->add_field('contextid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
152 $xmldb_table->add_field('component', XMLDB_TYPE_CHAR, 100, null, XMLDB_NOTNULL, null, null);
153 $xmldb_table->add_field('filearea', XMLDB_TYPE_CHAR, 50, null, XMLDB_NOTNULL, null, null);
154 $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
155 $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
156 $xmldb_table->add_field('newcontextid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
157 $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
158 $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
159 $xmldb_table->add_index('backupid_contextid_component_filearea_itemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','contextid','component','filearea','itemid'));
161 $dbman->create_temp_table($xmldb_table); // And create it
164 public static function drop_backup_ids_temp_table($backupid) {
165 global $DB;
166 $dbman = $DB->get_manager(); // We are going to use database_manager services
168 $targettablename = 'backup_ids_temp';
169 if ($dbman->table_exists($targettablename)) {
170 $table = new xmldb_table($targettablename);
171 $dbman->drop_table($table); // And drop it
176 * Decode the info field from backup_ids_temp or backup_files_temp.
178 * @param mixed $info The info field data to decode, may be an object or a simple integer.
179 * @return mixed The decoded information. For simple types it returns, for complex ones we decode.
181 public static function decode_backup_temp_info($info) {
182 // We encode all data except null.
183 if ($info != null) {
184 return unserialize(gzuncompress(base64_decode($info)));
186 return $info;
190 * Encode the info field for backup_ids_temp or backup_files_temp.
192 * @param mixed $info string The info field data to encode.
193 * @return string An encoded string of data or null if the input is null.
195 public static function encode_backup_temp_info($info) {
196 // We encode if there is any information to keep the translations simpler.
197 if ($info != null) {
198 // We compress if possible. It reduces db, network and memory storage. The saving is greater than CPU compression cost.
199 // Compression level 1 is chosen has it produces good compression with the smallest possible overhead, see MDL-40618.
200 return base64_encode(gzcompress(serialize($info), 1));
202 return $info;
206 * Given one type and id from controller, return the corresponding courseid
208 public static function get_courseid_from_type_id($type, $id) {
209 global $DB;
210 if ($type == backup::TYPE_1COURSE) {
211 return $id; // id is the course id
213 } else if ($type == backup::TYPE_1SECTION) {
214 if (! $courseid = $DB->get_field('course_sections', 'course', array('id' => $id))) {
215 throw new backup_dbops_exception('course_not_found_for_section', $id);
217 return $courseid;
218 } else if ($type == backup::TYPE_1ACTIVITY) {
219 if (! $courseid = $DB->get_field('course_modules', 'course', array('id' => $id))) {
220 throw new backup_dbops_exception('course_not_found_for_moduleid', $id);
222 return $courseid;
227 * Given one activity task, return the activity information and related settings
228 * Used by get_moodle_backup_information()
230 private static function get_activity_backup_information($task) {
232 $contentinfo = array(
233 'moduleid' => $task->get_moduleid(),
234 'sectionid' => $task->get_sectionid(),
235 'modulename' => $task->get_modulename(),
236 'title' => $task->get_name(),
237 'directory' => 'activities/' . $task->get_modulename() . '_' . $task->get_moduleid());
239 // Now get activity settings
240 // Calculate prefix to find valid settings
241 $prefix = basename($contentinfo['directory']);
242 $settingsinfo = array();
243 foreach ($task->get_settings() as $setting) {
244 // Discard ones without valid prefix
245 if (strpos($setting->get_name(), $prefix) !== 0) {
246 continue;
248 // Validate level is correct (activity)
249 if ($setting->get_level() != backup_setting::ACTIVITY_LEVEL) {
250 throw new backup_controller_exception('setting_not_activity_level', $setting);
252 $settinginfo = array(
253 'level' => 'activity',
254 'activity' => $prefix,
255 'name' => $setting->get_name(),
256 'value' => $setting->get_value());
257 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
259 return array($contentinfo, $settingsinfo);
263 * Given one section task, return the section information and related settings
264 * Used by get_moodle_backup_information()
266 private static function get_section_backup_information($task) {
268 $contentinfo = array(
269 'sectionid' => $task->get_sectionid(),
270 'title' => $task->get_name(),
271 'directory' => 'sections/' . 'section_' . $task->get_sectionid());
273 // Now get section settings
274 // Calculate prefix to find valid settings
275 $prefix = basename($contentinfo['directory']);
276 $settingsinfo = array();
277 foreach ($task->get_settings() as $setting) {
278 // Discard ones without valid prefix
279 if (strpos($setting->get_name(), $prefix) !== 0) {
280 continue;
282 // Validate level is correct (section)
283 if ($setting->get_level() != backup_setting::SECTION_LEVEL) {
284 throw new backup_controller_exception('setting_not_section_level', $setting);
286 $settinginfo = array(
287 'level' => 'section',
288 'section' => $prefix,
289 'name' => $setting->get_name(),
290 'value' => $setting->get_value());
291 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
293 return array($contentinfo, $settingsinfo);
297 * Given one course task, return the course information and related settings
298 * Used by get_moodle_backup_information()
300 private static function get_course_backup_information($task) {
302 $contentinfo = array(
303 'courseid' => $task->get_courseid(),
304 'title' => $task->get_name(),
305 'directory' => 'course');
307 // Now get course settings
308 // Calculate prefix to find valid settings
309 $prefix = basename($contentinfo['directory']);
310 $settingsinfo = array();
311 foreach ($task->get_settings() as $setting) {
312 // Discard ones without valid prefix
313 if (strpos($setting->get_name(), $prefix) !== 0) {
314 continue;
316 // Validate level is correct (course)
317 if ($setting->get_level() != backup_setting::COURSE_LEVEL) {
318 throw new backup_controller_exception('setting_not_course_level', $setting);
320 $settinginfo = array(
321 'level' => 'course',
322 'name' => $setting->get_name(),
323 'value' => $setting->get_value());
324 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
326 return array($contentinfo, $settingsinfo);
330 * Given one root task, return the course information and related settings
331 * Used by get_moodle_backup_information()
333 private static function get_root_backup_information($task) {
335 // Now get root settings
336 $settingsinfo = array();
337 foreach ($task->get_settings() as $setting) {
338 // Validate level is correct (root)
339 if ($setting->get_level() != backup_setting::ROOT_LEVEL) {
340 throw new backup_controller_exception('setting_not_root_level', $setting);
342 $settinginfo = array(
343 'level' => 'root',
344 'name' => $setting->get_name(),
345 'value' => $setting->get_value());
346 $settingsinfo[$setting->get_name()] = (object)$settinginfo;
348 return array(null, $settingsinfo);
352 * Get details information for main moodle_backup.xml file, extracting it from
353 * the specified controller.
355 * If you specify the progress monitor, this will start a new progress section
356 * to track progress in processing (in case this task takes a long time).
358 * @param string $backupid Backup ID
359 * @param \core\progress\base $progress Optional progress monitor
361 public static function get_moodle_backup_information($backupid,
362 \core\progress\base $progress = null) {
364 // Start tracking progress if required (for load_controller).
365 if ($progress) {
366 $progress->start_progress('get_moodle_backup_information', 2);
369 $detailsinfo = array(); // Information details
370 $contentsinfo= array(); // Information about backup contents
371 $settingsinfo= array(); // Information about backup settings
372 $bc = self::load_controller($backupid); // Load controller
374 // Note that we have loaded controller.
375 if ($progress) {
376 $progress->progress(1);
379 // Details info
380 $detailsinfo['id'] = $bc->get_id();
381 $detailsinfo['backup_id'] = $bc->get_backupid();
382 $detailsinfo['type'] = $bc->get_type();
383 $detailsinfo['format'] = $bc->get_format();
384 $detailsinfo['interactive'] = $bc->get_interactive();
385 $detailsinfo['mode'] = $bc->get_mode();
386 $detailsinfo['execution'] = $bc->get_execution();
387 $detailsinfo['executiontime'] = $bc->get_executiontime();
388 $detailsinfo['userid'] = $bc->get_userid();
389 $detailsinfo['courseid'] = $bc->get_courseid();
392 // Init content placeholders
393 $contentsinfo['activities'] = array();
394 $contentsinfo['sections'] = array();
395 $contentsinfo['course'] = array();
397 // Get tasks and start nested progress.
398 $tasks = $bc->get_plan()->get_tasks();
399 if ($progress) {
400 $progress->start_progress('get_moodle_backup_information', count($tasks));
401 $done = 1;
404 // Contents info (extract information from tasks)
405 foreach ($tasks as $task) {
407 if ($task instanceof backup_activity_task) { // Activity task
409 if ($task->get_setting_value('included')) { // Only return info about included activities
410 list($contentinfo, $settings) = self::get_activity_backup_information($task);
411 $contentsinfo['activities'][] = $contentinfo;
412 $settingsinfo = array_merge($settingsinfo, $settings);
415 } else if ($task instanceof backup_section_task) { // Section task
417 if ($task->get_setting_value('included')) { // Only return info about included sections
418 list($contentinfo, $settings) = self::get_section_backup_information($task);
419 $contentsinfo['sections'][] = $contentinfo;
420 $settingsinfo = array_merge($settingsinfo, $settings);
423 } else if ($task instanceof backup_course_task) { // Course task
425 list($contentinfo, $settings) = self::get_course_backup_information($task);
426 $contentsinfo['course'][] = $contentinfo;
427 $settingsinfo = array_merge($settingsinfo, $settings);
429 } else if ($task instanceof backup_root_task) { // Root task
431 list($contentinfo, $settings) = self::get_root_backup_information($task);
432 $settingsinfo = array_merge($settingsinfo, $settings);
435 // Report task handled.
436 if ($progress) {
437 $progress->progress($done++);
441 $bc->destroy(); // Always need to destroy controller to handle circular references
443 // Finish progress reporting.
444 if ($progress) {
445 $progress->end_progress();
446 $progress->end_progress();
449 return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo);
453 * Update CFG->backup_version and CFG->backup_release if change in
454 * version is detected.
456 public static function apply_version_and_release() {
457 global $CFG;
459 if ($CFG->backup_version < backup::VERSION) {
460 set_config('backup_version', backup::VERSION);
461 set_config('backup_release', backup::RELEASE);
466 * Given the backupid, detect if the backup includes "mnet" remote users or no
468 public static function backup_includes_mnet_remote_users($backupid) {
469 global $CFG, $DB;
471 $sql = "SELECT COUNT(*)
472 FROM {backup_ids_temp} b
473 JOIN {user} u ON u.id = b.itemid
474 WHERE b.backupid = ?
475 AND b.itemname = 'userfinal'
476 AND u.mnethostid != ?";
477 $count = $DB->count_records_sql($sql, array($backupid, $CFG->mnet_localhost_id));
478 return (int)(bool)$count;
482 * Given the backupid, determine whether this backup should include
483 * files from the moodle file storage system.
485 * @param string $backupid The ID of the backup.
486 * @return int Indicates whether files should be included in backups.
488 public static function backup_includes_files($backupid) {
489 // This function is called repeatedly in a backup with many files.
490 // Loading the controller is a nontrivial operation (in a large test
491 // backup it took 0.3 seconds), so we do a temporary cache of it within
492 // this request.
493 if (self::$includesfilescachebackupid === $backupid) {
494 return self::$includesfilescache;
497 // Load controller, get value, then destroy controller and return result.
498 self::$includesfilescachebackupid = $backupid;
499 $bc = self::load_controller($backupid);
500 self::$includesfilescache = $bc->get_include_files();
501 $bc->destroy();
502 return self::$includesfilescache;
506 * Given the backupid, detect if the backup contains references to external contents
508 * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
509 * @return int
511 public static function backup_includes_file_references($backupid) {
512 global $CFG, $DB;
514 $sql = "SELECT count(r.repositoryid)
515 FROM {files} f
516 LEFT JOIN {files_reference} r
517 ON r.id = f.referencefileid
518 JOIN {backup_ids_temp} bi
519 ON f.id = bi.itemid
520 WHERE bi.backupid = ?
521 AND bi.itemname = 'filefinal'";
522 $count = $DB->count_records_sql($sql, array($backupid));
523 return (int)(bool)$count;
527 * Given the courseid, return some course related information we want to transport
529 * @param int $course the id of the course this backup belongs to
531 public static function backup_get_original_course_info($courseid) {
532 global $DB;
533 return $DB->get_record('course', array('id' => $courseid), 'fullname, shortname, startdate, enddate, format');
537 * Sets the default values for the settings in a backup operation
539 * Based on the mode of the backup it will load proper defaults
540 * using {@link apply_admin_config_defaults}.
542 * @param backup_controller $controller
544 public static function apply_config_defaults(backup_controller $controller) {
545 // Based on the mode of the backup (general, automated, import, hub...)
546 // decide the action to perform to get defaults loaded
547 $mode = $controller->get_mode();
549 switch ($mode) {
550 case backup::MODE_GENERAL:
551 // Load the general defaults
552 $settings = array(
553 'backup_general_users' => 'users',
554 'backup_general_anonymize' => 'anonymize',
555 'backup_general_role_assignments' => 'role_assignments',
556 'backup_general_activities' => 'activities',
557 'backup_general_blocks' => 'blocks',
558 'backup_general_filters' => 'filters',
559 'backup_general_comments' => 'comments',
560 'backup_general_badges' => 'badges',
561 'backup_general_calendarevents' => 'calendarevents',
562 'backup_general_userscompletion' => 'userscompletion',
563 'backup_general_logs' => 'logs',
564 'backup_general_histories' => 'grade_histories',
565 'backup_general_questionbank' => 'questionbank',
566 'backup_general_groups' => 'groups',
567 'backup_general_competencies' => 'competencies'
569 self::apply_admin_config_defaults($controller, $settings, true);
570 break;
571 case backup::MODE_IMPORT:
572 // Load the import defaults.
573 $settings = array(
574 'backup_import_activities' => 'activities',
575 'backup_import_blocks' => 'blocks',
576 'backup_import_filters' => 'filters',
577 'backup_import_calendarevents' => 'calendarevents',
578 'backup_import_questionbank' => 'questionbank',
579 'backup_import_groups' => 'groups',
580 'backup_import_competencies' => 'competencies'
582 self::apply_admin_config_defaults($controller, $settings, true);
583 if ((!$controller->get_interactive()) &&
584 $controller->get_type() == backup::TYPE_1ACTIVITY) {
585 // This is duplicate - there is no concept of defaults - these settings must be on.
586 $settings = array(
587 'activities',
588 'blocks',
589 'filters',
590 'questionbank'
592 self::force_enable_settings($controller, $settings);
594 break;
595 case backup::MODE_AUTOMATED:
596 // Load the automated defaults.
597 $settings = array(
598 'backup_auto_users' => 'users',
599 'backup_auto_role_assignments' => 'role_assignments',
600 'backup_auto_activities' => 'activities',
601 'backup_auto_blocks' => 'blocks',
602 'backup_auto_filters' => 'filters',
603 'backup_auto_comments' => 'comments',
604 'backup_auto_badges' => 'badges',
605 'backup_auto_calendarevents' => 'calendarevents',
606 'backup_auto_userscompletion' => 'userscompletion',
607 'backup_auto_logs' => 'logs',
608 'backup_auto_histories' => 'grade_histories',
609 'backup_auto_questionbank' => 'questionbank',
610 'backup_auto_groups' => 'groups',
611 'backup_auto_competencies' => 'competencies'
613 self::apply_admin_config_defaults($controller, $settings, false);
614 break;
615 default:
616 // Nothing to do for other modes (HUB...). Some day we
617 // can define defaults (admin UI...) for them if we want to
622 * Turn these settings on. No defaults from admin settings.
624 * @param backup_controller $controller
625 * @param array $settings a map from admin config names to setting names (Config name => Setting name)
627 private static function force_enable_settings(backup_controller $controller, array $settings) {
628 $plan = $controller->get_plan();
629 foreach ($settings as $config => $settingname) {
630 $value = true;
631 if ($plan->setting_exists($settingname)) {
632 $setting = $plan->get_setting($settingname);
633 // We do not allow this setting to be locked for a duplicate function.
634 if ($setting->get_status() !== base_setting::NOT_LOCKED) {
635 $setting->set_status(base_setting::NOT_LOCKED);
637 $setting->set_value($value);
638 $setting->set_status(base_setting::LOCKED_BY_CONFIG);
639 } else {
640 $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
646 * Sets the controller settings default values from the admin config.
648 * @param backup_controller $controller
649 * @param array $settings a map from admin config names to setting names (Config name => Setting name)
650 * @param boolean $uselocks whether "locked" admin settings should be honoured
652 private static function apply_admin_config_defaults(backup_controller $controller, array $settings, $uselocks) {
653 $plan = $controller->get_plan();
654 foreach ($settings as $config=>$settingname) {
655 $value = get_config('backup', $config);
656 if ($value === false) {
657 // Ignore this because the config has not been set. get_config
658 // returns false if a setting doesn't exist, '0' is returned when
659 // the configuration is set to false.
660 $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG);
661 continue;
663 $locked = $uselocks && (get_config('backup', $config.'_locked') == true);
664 if ($plan->setting_exists($settingname)) {
665 $setting = $plan->get_setting($settingname);
666 // We can only update the setting if it isn't already locked by config or permission.
667 if ($setting->get_status() !== base_setting::LOCKED_BY_CONFIG
668 && $setting->get_status() !== base_setting::LOCKED_BY_PERMISSION) {
669 $setting->set_value($value);
670 if ($locked) {
671 $setting->set_status(base_setting::LOCKED_BY_CONFIG);
674 } else {
675 $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
681 * Get the progress details of a backup operation.
682 * Get backup records directly from database, if the backup has successfully completed
683 * there will be no controller object to load.
685 * @param string $backupid The backup id to query.
686 * @return array $progress The backup progress details.
688 public static function get_progress($backupid) {
689 global $DB;
691 $progress = array();
692 $backuprecord = $DB->get_record(
693 'backup_controllers',
694 array('backupid' => $backupid),
695 'status, progress, operation',
696 MUST_EXIST);
698 $status = $backuprecord->status;
699 $progress = $backuprecord->progress;
700 $operation = $backuprecord->operation;
702 $progress = array('status' => $status, 'progress' => $progress, 'backupid' => $backupid, 'operation' => $operation);
704 return $progress;