3 // This file is part of Moodle - http://moodle.org/
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.
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/>.
20 * @subpackage backup-moodle2
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
26 * Define all the backup steps that will be used by common tasks in backup
30 * create the temp dir where backup/restore will happen,
31 * delete old directories and create temp ids table
33 class create_and_clean_temp_stuff extends backup_execution_step {
35 protected function define_execution() {
36 backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
37 backup_helper::clear_backup_dir($this->get_backupid()); // Empty temp dir, just in case
38 backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
39 backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
44 * delete the temp dir used by backup/restore (conditionally),
45 * delete old directories and drop tem ids table. Note we delete
46 * the directory but not the correspondig log file that will be
47 * there for, at least, 4 hours - only delete_old_backup_dirs()
48 * deletes log files (for easier access to them)
50 class drop_and_clean_temp_stuff extends backup_execution_step {
52 protected function define_execution() {
54 backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
55 backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
56 if (empty($CFG->keeptempdirectoriesonbackup)) { // Conditionally
57 backup_helper::delete_backup_dir($this->get_backupid()); // Empty backup dir
63 * Create the directory where all the task (activity/block...) information will be stored
65 class create_taskbasepath_directory extends backup_execution_step {
67 protected function define_execution() {
69 $basepath = $this->task->get_taskbasepath();
70 if (!check_dir_exists($basepath, true, true)) {
71 throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
77 * Abtract tructure step, parent of all the activity structure steps. Used to wrap the
78 * activity structure definition within the main <activity ...> tag. Also provides
79 * subplugin support for activities (that must be properly declared)
81 abstract class backup_activity_structure_step extends backup_structure_step {
83 protected function add_subplugin_structure($subpluginname, $element, $multiple) {
87 // Check the requested subpluginname is a valid one
88 $subpluginsfile = $CFG->dirroot . '/mod/' . $this->task->get_modulename() . '/db/subplugins.php';
89 if (!file_exists($subpluginsfile)) {
90 throw new backup_step_exception('activity_missing_subplugins_php_file', $this->task->get_modulename());
92 include($subpluginsfile);
93 if (!array_key_exists($subpluginname, $subplugins)) {
94 throw new backup_step_exception('incorrect_subplugin_type', $subpluginname);
97 // Arrived here, subplugin is correct, let's create the optigroup
98 $optigroupname = $subpluginname . '_' . $element->get_name() . '_subplugin';
99 $optigroup = new backup_optigroup($optigroupname, null, $multiple);
101 // Get all the optigroup_elements, looking across al the subplugin dirs
103 $subpluginsdirs = get_plugin_list($subpluginname);
104 foreach ($subpluginsdirs as $name => $subpluginsdir) {
105 $classname = 'backup_' . $subpluginname . '_' . $name . '_subplugin';
106 $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
107 if (file_exists($backupfile)) {
108 require_once($backupfile);
109 $backupsubplugin = new $classname($subpluginname, $name);
110 // Add subplugin returned structure to optigroup (must be optigroup_element instance)
111 if ($subpluginstructure = $backupsubplugin->define_subplugin_structure($element->get_name())) {
112 $optigroup->add_child($subpluginstructure);
116 // Finished, add optigroup to element
117 $element->add_child($optigroup);
120 protected function prepare_activity_structure($activitystructure) {
122 // Create the wrap element
123 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
126 $activity->add_child($activitystructure);
129 $activityarr = array((object)array(
130 'id' => $this->task->get_activityid(),
131 'moduleid' => $this->task->get_moduleid(),
132 'modulename' => $this->task->get_modulename(),
133 'contextid' => $this->task->get_contextid()));
135 $activity->set_source_array($activityarr);
137 // Return the root element (activity)
143 * Abtract structure step, parent of all the block structure steps. Used to wrap the
144 * block structure definition within the main <block ...> tag
146 abstract class backup_block_structure_step extends backup_structure_step {
148 protected function prepare_block_structure($blockstructure) {
150 // Create the wrap element
151 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
154 $block->add_child($blockstructure);
157 $blockarr = array((object)array(
158 'id' => $this->task->get_blockid(),
159 'blockname' => $this->task->get_blockname(),
160 'contextid' => $this->task->get_contextid()));
162 $block->set_source_array($blockarr);
164 // Return the root element (block)
170 * structure step that will generate the module.xml file for the activity,
171 * acummulating various information about the activity, annotating groupings
172 * and completion/avail conf
174 class backup_module_structure_step extends backup_structure_step {
176 protected function define_structure() {
178 // Define each element separated
180 $module = new backup_nested_element('module', array('id', 'version'), array(
181 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
182 'added', 'score', 'indent', 'visible',
183 'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
184 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
185 'availablefrom', 'availableuntil', 'showavailability'));
187 $availinfo = new backup_nested_element('availability_info');
188 $availability = new backup_nested_element('availability', array('id'), array(
189 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
192 $module->add_child($availinfo);
193 $availinfo->add_child($availability);
197 $module->set_source_sql('
198 SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
199 FROM {course_modules} cm
200 JOIN {modules} m ON m.id = cm.module
201 JOIN {course_sections} s ON s.id = cm.section
202 WHERE cm.id = ?', array(backup::VAR_MODID));
204 $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
206 // Define annotations
207 $module->annotate_ids('grouping', 'groupingid');
209 // Return the root element ($module)
215 * structure step that will genereate the section.xml file for the section
218 class backup_section_structure_step extends backup_structure_step {
220 protected function define_structure() {
222 // Define each element separated
224 $section = new backup_nested_element('section', array('id'), array(
225 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
229 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
232 $section->set_source_alias('section', 'number');
235 $section->annotate_files('course', 'section', 'id');
242 * structure step that will generate the course.xml file for the course, including
243 * course category reference, tags, modules restriction information
244 * and some annotations (files & groupings)
246 class backup_course_structure_step extends backup_structure_step {
248 protected function define_structure() {
251 // Define each element separated
253 $course = new backup_nested_element('course', array('id', 'contextid'), array(
254 'shortname', 'fullname', 'idnumber',
255 'summary', 'summaryformat', 'format', 'showgrades',
256 'newsitems', 'startdate',
257 'numsections', 'marker', 'maxbytes', 'showreports',
258 'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
259 'defaultgroupingid', 'lang', 'theme',
260 'timecreated', 'timemodified',
261 'requested', 'restrictmodules',
262 'enablecompletion'));
264 $category = new backup_nested_element('category', array('id'), array(
265 'name', 'description'));
267 $tags = new backup_nested_element('tags');
269 $tag = new backup_nested_element('tag', array('id'), array(
272 $allowedmodules = new backup_nested_element('allowed_modules');
274 $module = new backup_nested_element('module', array('modulename'));
278 $course->add_child($category);
280 $course->add_child($tags);
281 $tags->add_child($tag);
283 $course->add_child($allowedmodules);
284 $allowedmodules->add_child($module);
288 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
289 $courserec->contextid = $this->task->get_contextid();
291 $course->set_source_array(array($courserec));
293 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
295 $category->set_source_array(array($categoryrec));
297 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
299 JOIN {tag_instance} ti ON ti.tagid = t.id
300 WHERE ti.itemtype = ?
301 AND ti.itemid = ?', array(
302 $this->is_sqlparam('course'),
303 backup::VAR_PARENTID));
305 $module->set_source_sql('SELECT m.name AS modulename
307 JOIN {course_allowed_modules} cam ON m.id = cam.module
308 WHERE course = ?', array(backup::VAR_COURSEID));
312 $course->annotate_ids('grouping', 'defaultgroupingid');
314 $course->annotate_files('course', 'summary', null);
315 $course->annotate_files('course', 'legacy', null);
317 // Return root element ($course)
324 * structure step that will generate the roles.xml file for the given context, observing
325 * the role_assignments setting to know if that part needs to be included
327 class backup_roles_structure_step extends backup_structure_step {
329 protected function define_structure() {
331 // To know if we are including role assignments
332 $roleassignments = $this->get_setting_value('role_assignments');
334 // Define each element separated
336 $roles = new backup_nested_element('roles');
338 $overrides = new backup_nested_element('role_overrides');
340 $override = new backup_nested_element('override', array('id'), array(
341 'roleid', 'capability', 'permission', 'timemodified',
344 $assignments = new backup_nested_element('role_assignments');
346 $assignment = new backup_nested_element('assignment', array('id'), array(
347 'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
351 $roles->add_child($overrides);
352 $roles->add_child($assignments);
354 $overrides->add_child($override);
355 $assignments->add_child($assignment);
359 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
361 // Assignments only added if specified
362 if ($roleassignments) {
363 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
366 // Define id annotations
367 $override->annotate_ids('role', 'roleid');
369 $assignment->annotate_ids('role', 'roleid');
371 $assignment->annotate_ids('user', 'userid');
378 * structure step that will generate the roles.xml containing the
379 * list of roles used along the whole backup process. Just raw
380 * list of used roles from role table
382 class backup_final_roles_structure_step extends backup_structure_step {
384 protected function define_structure() {
388 $rolesdef = new backup_nested_element('roles_definition');
390 $role = new backup_nested_element('role', array('id'), array(
391 'name', 'shortname', 'nameincourse', 'description',
392 'sortorder', 'archetype'));
396 $rolesdef->add_child($role);
400 $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
402 JOIN {backup_ids_temp} bi ON r.id = bi.itemid
403 LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
404 WHERE bi.backupid = ?
405 AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
407 // Return main element (rolesdef)
413 * structure step that will generate the scales.xml containing the
414 * list of scales used along the whole backup process.
416 class backup_final_scales_structure_step extends backup_structure_step {
418 protected function define_structure() {
422 $scalesdef = new backup_nested_element('scales_definition');
424 $scale = new backup_nested_element('scale', array('id'), array(
425 'courseid', 'userid', 'name', 'scale',
426 'description', 'descriptionformat', 'timemodified'));
430 $scalesdef->add_child($scale);
434 $scale->set_source_sql("SELECT s.*
436 JOIN {backup_ids_temp} bi ON s.id = bi.itemid
437 WHERE bi.backupid = ?
438 AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
440 // Return main element (scalesdef)
446 * structure step that will generate the outcomes.xml containing the
447 * list of outcomes used along the whole backup process.
449 class backup_final_outcomes_structure_step extends backup_structure_step {
451 protected function define_structure() {
455 $outcomesdef = new backup_nested_element('outcomes_definition');
457 $outcome = new backup_nested_element('outcome', array('id'), array(
458 'courseid', 'userid', 'shortname', 'fullname',
459 'scaleid', 'description', 'descriptionformat', 'timecreated',
460 'timemodified','usermodified'));
464 $outcomesdef->add_child($outcome);
468 $outcome->set_source_sql("SELECT o.*
469 FROM {grade_outcomes} o
470 JOIN {backup_ids_temp} bi ON o.id = bi.itemid
471 WHERE bi.backupid = ?
472 AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
474 // Return main element (outcomesdef)
480 * structure step in charge of constructing the filters.xml file for all the filters found
483 class backup_filters_structure_step extends backup_structure_step {
485 protected function define_structure() {
487 // Define each element separated
489 $filters = new backup_nested_element('filters');
491 $actives = new backup_nested_element('filter_actives');
493 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
495 $configs = new backup_nested_element('filter_configs');
497 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
501 $filters->add_child($actives);
502 $filters->add_child($configs);
504 $actives->add_child($active);
505 $configs->add_child($config);
509 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
511 $active->set_source_array($activearr);
512 $config->set_source_array($configarr);
514 // Return the root element (filters)
520 * structure step in charge of constructing the comments.xml file for all the comments found
523 class backup_comments_structure_step extends backup_structure_step {
525 protected function define_structure() {
527 // Define each element separated
529 $comments = new backup_nested_element('comments');
531 $comment = new backup_nested_element('comment', array('id'), array(
532 'commentarea', 'itemid', 'content', 'format',
533 'userid', 'timecreated'));
537 $comments->add_child($comment);
541 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
543 // Define id annotations
545 $comment->annotate_ids('user', 'userid');
547 // Return the root element (comments)
553 * structure step in charge if constructing the completion.xml file for all the users completion
554 * information in a given activity
556 class backup_userscompletion_structure_step extends backup_structure_step {
558 protected function define_structure() {
560 // Define each element separated
562 $completions = new backup_nested_element('completions');
564 $completion = new backup_nested_element('completion', array('id'), array(
565 'userid', 'completionstate', 'viewed', 'timemodified'));
569 $completions->add_child($completion);
573 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
575 // Define id annotations
577 $completion->annotate_ids('user', 'userid');
579 // Return the root element (completions)
585 * structure step in charge of constructing the main groups.xml file for all the groups and
586 * groupings information already annotated
588 class backup_groups_structure_step extends backup_structure_step {
590 protected function define_structure() {
592 // To know if we are including users
593 $users = $this->get_setting_value('users');
595 // Define each element separated
597 $groups = new backup_nested_element('groups');
599 $group = new backup_nested_element('group', array('id'), array(
600 'name', 'description', 'descriptionformat', 'enrolmentkey',
601 'picture', 'hidepicture', 'timecreated', 'timemodified'));
603 $members = new backup_nested_element('group_members');
605 $member = new backup_nested_element('group_member', array('id'), array(
606 'userid', 'timeadded'));
608 $groupings = new backup_nested_element('groupings');
610 $grouping = new backup_nested_element('grouping', 'id', array(
611 'name', 'description', 'descriptionformat', 'configdata',
612 'timecreated', 'timemodified'));
614 $groupinggroups = new backup_nested_element('grouping_groups');
616 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
617 'groupid', 'timeadded'));
621 $groups->add_child($group);
622 $groups->add_child($groupings);
624 $group->add_child($members);
625 $members->add_child($member);
627 $groupings->add_child($grouping);
628 $grouping->add_child($groupinggroups);
629 $groupinggroups->add_child($groupinggroup);
633 $group->set_source_sql("
636 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
637 WHERE bi.backupid = ?
638 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
640 // This only happens if we are including users
642 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
645 $grouping->set_source_sql("
648 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
649 WHERE bi.backupid = ?
650 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
652 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
654 // Define id annotations (as final)
656 $member->annotate_ids('userfinal', 'userid');
658 // Define file annotations
660 //TODO: not implemented yet
661 $group->annotate_files('group', 'description', 'id');
662 $group->annotate_files('group', 'image', 'id');
664 // Return the root element (groups)
670 * structure step in charge of constructing the main users.xml file for all the users already
671 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
674 class backup_users_structure_step extends backup_structure_step {
676 protected function define_structure() {
679 // To know if we are anonymizing users
680 $anonymize = $this->get_setting_value('anonymize');
681 // To know if we are including role assignments
682 $roleassignments = $this->get_setting_value('role_assignments');
684 // Define each element separated
686 $users = new backup_nested_element('users');
688 // Create the array of user fields by hand, as far as we have various bits to control
689 // anonymize option, password backup, mnethostid...
691 // First, the fields not needing anonymization nor special handling
692 $normalfields = array(
693 'confirmed', 'policyagreed', 'deleted',
694 'lang', 'theme', 'timezone', 'firstaccess',
695 'lastaccess', 'lastlogin', 'currentlogin', 'secret',
696 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
697 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
698 'timemodified', 'trustbitmask', 'screenreader');
700 // Then, the fields potentially needing anonymization
702 'username', 'idnumber', 'firstname', 'lastname',
703 'email', 'emailstop', 'lastip', 'picture',
704 'url', 'description', 'description_format', 'imagealt', 'auth');
706 // Add anonymized fields to $userfields with custom final element
707 foreach ($anonfields as $field) {
709 $userfields[] = new anonymizer_final_element($field);
711 $userfields[] = $field; // No anonymization, normally added
715 // mnethosturl requires special handling (custom final element)
716 $userfields[] = new mnethosturl_final_element('mnethosturl');
718 // password added conditionally
719 if (!empty($CFG->includeuserpasswordsinbackup)) {
720 $userfields[] = 'password';
723 // Merge all the fields
724 $userfields = array_merge($userfields, $normalfields);
726 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
728 $customfields = new backup_nested_element('custom_fields');
730 $customfield = new backup_nested_element('custom_field', array('id'), array(
731 'field_name', 'field_type', 'field_data'));
733 $tags = new backup_nested_element('tags');
735 $tag = new backup_nested_element('tag', array('id'), array(
738 $preferences = new backup_nested_element('preferences');
740 $preference = new backup_nested_element('preference', array('id'), array(
743 $roles = new backup_nested_element('roles');
745 $overrides = new backup_nested_element('role_overrides');
747 $override = new backup_nested_element('override', array('id'), array(
748 'roleid', 'capability', 'permission', 'timemodified',
751 $assignments = new backup_nested_element('role_assignments');
753 $assignment = new backup_nested_element('assignment', array('id'), array(
754 'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
759 $users->add_child($user);
761 $user->add_child($customfields);
762 $customfields->add_child($customfield);
764 $user->add_child($tags);
765 $tags->add_child($tag);
767 $user->add_child($preferences);
768 $preferences->add_child($preference);
770 $user->add_child($roles);
772 $roles->add_child($overrides);
773 $roles->add_child($assignments);
775 $overrides->add_child($override);
776 $assignments->add_child($assignment);
780 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
782 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
783 JOIN {context} c ON c.instanceid = u.id
784 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
785 WHERE bi.backupid = ?
787 AND c.contextlevel = ?', array(
788 $this->is_sqlparam($this->get_backupid()),
789 $this->is_sqlparam('userfinal'),
790 $this->is_sqlparam(CONTEXT_USER)));
792 // All the rest on information is only added if we arent
793 // in an anonymized backup
795 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
796 FROM {user_info_field} f
797 JOIN {user_info_data} d ON d.fieldid = f.id
798 WHERE d.userid = ?', array(backup::VAR_PARENTID));
800 $customfield->set_source_alias('shortname', 'field_name');
801 $customfield->set_source_alias('datatype', 'field_type');
802 $customfield->set_source_alias('data', 'field_data');
804 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
806 JOIN {tag_instance} ti ON ti.tagid = t.id
807 WHERE ti.itemtype = ?
808 AND ti.itemid = ?', array(
809 $this->is_sqlparam('user'),
810 backup::VAR_PARENTID));
812 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
814 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
816 // Assignments only added if specified
817 if ($roleassignments) {
818 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
821 // Define id annotations (as final)
822 $override->annotate_ids('rolefinal', 'roleid');
825 // Return root element (users)
831 * structure step in charge of constructing the block.xml file for one
832 * given block (intance and positions). If the block has custom DB structure
833 * that will go to a separate file (different step defined in block class)
835 class backup_block_instance_structure_step extends backup_structure_step {
837 protected function define_structure() {
840 // Define each element separated
842 $block = new backup_nested_element('block', array('id', 'version'), array(
843 'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
844 'defaultregion', 'defaultweight', 'configdata'));
846 $positions = new backup_nested_element('block_positions', null, array(
847 'contextid', 'pagetype', 'subpage', 'visible',
848 'region', 'weight'));
852 $block->add_child($positions);
854 // Transform configdata information if needed (process links and friends)
855 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
856 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
857 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
858 foreach ($configdata as $attribute => $value) {
859 if (in_array($attribute, $attrstotransform)) {
860 $configdata[$attribute] = $this->contenttransformer->process($value);
863 $blockrec->configdata = base64_encode(serialize((object)$configdata));
865 // Get the version of the block
866 $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
870 $block->set_source_array(array($blockrec));
872 $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
874 // Return the root element (block)
880 * structure step in charge of constructing the logs.xml file for all the log records found
883 class backup_activity_logs_structure_step extends backup_structure_step {
885 protected function define_structure() {
887 // Define each element separated
889 $logs = new backup_nested_element('logs');
891 $log = new backup_nested_element('log', array('id'), array(
892 'time', 'userid', 'ip', 'module',
893 'action', 'url', 'info'));
897 $logs->add_child($log);
901 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
904 // NOTE: We don't annotate users from logs as far as they MUST be
905 // always annotated by the activity.
907 // Return the root element (logs)
914 * structure in charge of constructing the inforef.xml file for all the items we want
915 * to have referenced there (users, roles, files...)
917 class backup_inforef_structure_step extends backup_structure_step {
919 protected function define_structure() {
921 // Items we want to include in the inforef file. NOTE: Important to keep this
922 // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
923 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
927 $inforef = new backup_nested_element('inforef');
929 // For each item, conditionally, if there are already records, build element
930 foreach ($items as $itemname) {
931 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
932 $elementroot = new backup_nested_element($itemname . 'ref');
933 $element = new backup_nested_element($itemname, array('id'));
934 $inforef->add_child($elementroot);
935 $elementroot->add_child($element);
936 $element->set_source_sql("
938 FROM {backup_ids_temp}
941 array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
945 // We don't annotate anything there, but rely in the next step
946 // (move_inforef_annotations_to_final) that will change all the
947 // already saved 'inforref' entries to their 'final' annotations.
953 * This step will get all the annotations already processed to inforef.xml file and
954 * transform them into 'final' annotations.
956 class move_inforef_annotations_to_final extends backup_execution_step {
958 protected function define_execution() {
960 // Items we want to include in the inforef file. NOTE: Important to keep this
961 // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
962 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
963 foreach ($items as $itemname) {
965 backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
971 * structure in charge of constructing the files.xml file with all the
972 * annotated (final) files along the process. At, the same time, and
973 * using one specialised nested_element, will copy them form moodle storage
976 class backup_final_files_structure_step extends backup_structure_step {
978 protected function define_structure() {
982 $files = new backup_nested_element('files');
984 $file = new file_nested_element('file', array('id'), array(
985 'contenthash', 'contextid', 'component', 'filearea', 'itemid',
986 'filepath', 'filename', 'userid', 'filesize',
987 'mimetype', 'status', 'timecreated', 'timemodified',
988 'source', 'author', 'license', 'sortorder'));
992 $files->add_child($file);
996 $file->set_source_sql("SELECT f.*
998 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
999 WHERE bi.backupid = ?
1000 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
1007 * Structure step in charge of creating the main moodle_backup.xml file
1008 * where all the information related to the backup, settings, license and
1009 * other information needed on restore is added*/
1010 class backup_main_structure_step extends backup_structure_step {
1012 protected function define_structure() {
1018 $info['name'] = $this->get_setting_value('filename');
1019 $info['moodle_version'] = $CFG->version;
1020 $info['moodle_release'] = $CFG->release;
1021 $info['backup_version'] = $CFG->backup_version;
1022 $info['backup_release'] = $CFG->backup_release;
1023 $info['backup_date'] = time();
1024 $info['backup_uniqueid']= $this->get_backupid();
1025 $info['original_wwwroot']=$CFG->wwwroot;
1026 $info['original_site_identifier'] = get_site_identifier();
1027 $info['original_course_id'] = $this->get_courseid();
1029 // Get more information from controller
1030 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
1034 $moodle_backup = new backup_nested_element('moodle_backup');
1036 $information = new backup_nested_element('information', null, array(
1037 'name', 'moodle_version', 'moodle_release', 'backup_version',
1038 'backup_release', 'backup_date', 'original_wwwroot',
1039 'original_site_identifier', 'original_course_id'));
1041 $details = new backup_nested_element('details');
1043 $detail = new backup_nested_element('detail', array('backup_id'), array(
1044 'type', 'format', 'interactive', 'mode',
1045 'execution', 'executiontime'));
1047 $contents = new backup_nested_element('contents');
1049 $activities = new backup_nested_element('activities');
1051 $activity = new backup_nested_element('activity', null, array(
1052 'moduleid', 'sectionid', 'modulename', 'title',
1055 $sections = new backup_nested_element('sections');
1057 $section = new backup_nested_element('section', null, array(
1058 'sectionid', 'title', 'directory'));
1060 $course = new backup_nested_element('course', null, array(
1061 'courseid', 'title', 'directory'));
1063 $settings = new backup_nested_element('settings');
1065 $setting = new backup_nested_element('setting', null, array(
1066 'level', 'activity', 'name', 'value'));
1070 $moodle_backup->add_child($information);
1072 $information->add_child($details);
1073 $details->add_child($detail);
1075 $information->add_child($contents);
1076 if (!empty($cinfo['activities'])) {
1077 $contents->add_child($activities);
1078 $activities->add_child($activity);
1080 if (!empty($cinfo['sections'])) {
1081 $contents->add_child($sections);
1082 $sections->add_child($section);
1084 if (!empty($cinfo['course'])) {
1085 $contents->add_child($course);
1088 $information->add_child($settings);
1089 $settings->add_child($setting);
1094 $information->set_source_array(array((object)$info));
1096 $detail->set_source_array($dinfo);
1098 $activity->set_source_array($cinfo['activities']);
1100 $section->set_source_array($cinfo['sections']);
1102 $course->set_source_array($cinfo['course']);
1104 $setting->set_source_array($sinfo);
1106 // Prepare some information to be sent to main moodle_backup.xml file
1107 return $moodle_backup;
1113 * Execution step that will generate the final zip file with all the contents
1115 class backup_zip_contents extends backup_execution_step {
1117 protected function define_execution() {
1120 $basepath = $this->get_basepath();
1122 // Get the list of files in directory
1123 $filestemp = get_directory_list($basepath, '', false, true, true);
1125 foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1126 $files[$file] = $basepath . '/' . $file;
1129 // Add the log file if exists
1130 $logfilepath = $basepath . '.log';
1131 if (file_exists($logfilepath)) {
1132 $files['moodle_backup.log'] = $logfilepath;
1135 // Calculate the zip fullpath (in OS temp area it's always backup.zip)
1136 $zipfile = $basepath . '/backup.zip';
1138 // Get the zip packer
1139 $zippacker = get_file_packer('application/zip');
1142 $zippacker->archive_to_pathname($files, $zipfile);
1147 * This step will send the generated backup file to its final destination
1149 class backup_store_backup_file extends backup_execution_step {
1151 protected function define_execution() {
1154 $basepath = $this->get_basepath();
1156 // Calculate the zip fullpath (in OS temp area it's always backup.zip)
1157 $zipfile = $basepath . '/backup.zip';
1159 // Perform storage and return it (TODO: shouldn't be array but proper result object)
1160 return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
1166 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1167 * and put them to the backup_ids tables, to be used later as base to backup them
1169 class backup_activity_grade_items_to_ids extends backup_execution_step {
1171 protected function define_execution() {
1173 // Fetch all activity grade items
1174 if ($items = grade_item::fetch_all(array(
1175 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1176 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1177 // Annotate them in backup_ids
1178 foreach ($items as $item) {
1179 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1186 * This step will annotate all the groups belonging to already annotated groupings
1188 class backup_annotate_groups_from_groupings extends backup_execution_step {
1190 protected function define_execution() {
1193 // Fetch all the annotated groupings
1194 if ($groupings = $DB->get_records('backup_ids_temp', array(
1195 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1196 foreach ($groupings as $grouping) {
1197 if ($groups = $DB->get_records('groupings_groups', array(
1198 'groupingid' => $grouping->itemid))) {
1199 foreach ($groups as $group) {
1200 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1209 * This step will annotate all the scales belonging to already annotated outcomes
1211 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1213 protected function define_execution() {
1216 // Fetch all the annotated outcomes
1217 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1218 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1219 foreach ($outcomes as $outcome) {
1220 if ($scale = $DB->get_record('grade_outcomes', array(
1221 'id' => $outcome->itemid))) {
1222 // Annotate as scalefinal because it's > 0
1223 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1231 * This step will generate all the user annotations for the already
1232 * annottated (final) users. Need to do this here because each user
1233 * has its own context and structure tasks only are able to handle
1234 * one context. Also, this step will guarantee that every user has
1235 * its context created (req for other steps)
1237 class backup_annotate_all_user_files extends backup_execution_step {
1239 protected function define_execution() {
1242 // List of fileareas we are going to annotate
1243 // TODO: user image not implemented yet
1244 $fileareas = array('private', 'profile', 'image');
1246 // Fetch all annotated (final) users
1247 $rs = $DB->get_recordset('backup_ids_temp', array(
1248 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1249 foreach ($rs as $record) {
1250 $userid = $record->itemid;
1251 $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1252 // Proceed with every user filearea
1253 foreach ($fileareas as $filearea) {
1254 // We don't need to specify itemid ($userid - 4th param) as far as by
1255 // context we can get all the associated files. See MDL-22092
1256 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, 'user', $filearea, null);
1264 * structure step in charge of constructing the grades.xml file for all the grade items
1265 * and letters related to one activity
1267 class backup_activity_grades_structure_step extends backup_structure_step {
1269 protected function define_structure() {
1271 // To know if we are including userinfo
1272 $userinfo = $this->get_setting_value('userinfo');
1274 // Define each element separated
1276 $book = new backup_nested_element('activity_gradebook');
1278 $items = new backup_nested_element('grade_items');
1280 $item = new backup_nested_element('grade_item', array('id'), array(
1281 'categoryid', 'itemname', 'itemtype', 'itemmodule',
1282 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1283 'calculation', 'gradetype', 'grademax', 'grademin',
1284 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1285 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1286 'decimals', 'hidden', 'locked', 'locktime',
1287 'needsupdate', 'timecreated', 'timemodified'));
1289 $grades = new backup_nested_element('grade_grades');
1291 $grade = new backup_nested_element('grade_grade', array('id'), array(
1292 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1293 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1294 'locked', 'locktime', 'exported', 'overridden',
1295 'excluded', 'feedback', 'feedbackformat', 'information',
1296 'informationformat', 'timecreated', 'timemodified'));
1298 $letters = new backup_nested_element('grade_letters');
1300 $letter = new backup_nested_element('grade_letter', 'id', array(
1301 'lowerboundary', 'letter'));
1305 $book->add_child($items);
1306 $items->add_child($item);
1308 $item->add_child($grades);
1309 $grades->add_child($grade);
1311 $book->add_child($letters);
1312 $letters->add_child($letter);
1316 $item->set_source_sql("
1318 FROM {grade_items} gi
1319 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1320 WHERE bi.backupid = ?
1321 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1323 // This only happens if we are including user info
1325 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1328 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1332 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1333 $item->annotate_ids('outcome', 'outcomeid');
1335 $grade->annotate_ids('user', 'userid');
1336 $grade->annotate_ids('user', 'usermodified');
1338 // Return the root element (book)