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
80 abstract class backup_activity_structure_step extends backup_structure_step {
82 protected function prepare_activity_structure($activitystructure) {
84 // Create the wrap element
85 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
88 $activity->add_child($activitystructure);
91 $activityarr = array((object)array(
92 'id' => $this->task->get_activityid(),
93 'moduleid' => $this->task->get_moduleid(),
94 'modulename' => $this->task->get_modulename(),
95 'contextid' => $this->task->get_contextid()));
97 $activity->set_source_array($activityarr);
99 // Return the root element (activity)
105 * Abtract structure step, parent of all the block structure steps. Used to wrap the
106 * block structure definition within the main <block ...> tag
108 abstract class backup_block_structure_step extends backup_structure_step {
110 protected function prepare_block_structure($blockstructure) {
112 // Create the wrap element
113 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
116 $block->add_child($blockstructure);
119 $blockarr = array((object)array(
120 'id' => $this->task->get_blockid(),
121 'blockname' => $this->task->get_blockname(),
122 'contextid' => $this->task->get_contextid()));
124 $block->set_source_array($blockarr);
126 // Return the root element (block)
132 * structure step that will generate the module.xml file for the activity,
133 * acummulating various information about the activity, annotating groupings
134 * and completion/avail conf
136 class backup_module_structure_step extends backup_structure_step {
138 protected function define_structure() {
140 // Define each element separated
142 $module = new backup_nested_element('module', array('id', 'version'), array(
143 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
144 'added', 'score', 'indent', 'visible',
145 'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
146 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
147 'availablefrom', 'availableuntil', 'showavailability'));
149 $availinfo = new backup_nested_element('availability_info');
150 $availability = new backup_nested_element('availability', array('id'), array(
151 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
154 $module->add_child($availinfo);
155 $availinfo->add_child($availability);
159 $module->set_source_sql('
160 SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
161 FROM {course_modules} cm
162 JOIN {modules} m ON m.id = cm.module
163 JOIN {course_sections} s ON s.id = cm.section
164 WHERE cm.id = ?', array(backup::VAR_MODID));
166 $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
168 // Define annotations
169 $module->annotate_ids('grouping', 'groupingid');
171 // Return the root element ($module)
177 * structure step that will genereate the section.xml file for the section
180 class backup_section_structure_step extends backup_structure_step {
182 protected function define_structure() {
184 // Define each element separated
186 $section = new backup_nested_element('section', array('id'), array(
187 'number', 'name', 'summary', 'sequence', 'visible'));
191 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
194 $section->set_source_alias('section', 'number');
197 $section->annotate_files(array('course_section'), 'id');
204 * structure step that will generate the course.xml file for the course, including
205 * course category reference, tags, metacourse, modules restriction information
206 * and some annotations (files & groupings)
208 class backup_course_structure_step extends backup_structure_step {
210 protected function define_structure() {
213 // Define each element separated
215 $course = new backup_nested_element('course', array('id', 'contextid'), array(
216 'shortname', 'fullname', 'idnumber', 'password',
217 'summary', 'summaryformat', 'format', 'showgrades',
218 'newsitems', 'guest', 'startdate', 'enrolperiod',
219 'numsections', 'marker', 'maxbytes', 'showreports',
220 'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
221 'defaultgroupingid', 'lang', 'theme', 'cost',
222 'currency', 'timecreated', 'timemodified', 'metacourse',
223 'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
224 'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
225 'enrol', 'defaultrole', 'enablecompletion'));
227 $category = new backup_nested_element('category', array('id'), array(
228 'name', 'description'));
230 $tags = new backup_nested_element('tags');
232 $tag = new backup_nested_element('tag', array('id'), array(
235 $allowedmodules = new backup_nested_element('allowed_modules');
237 $module = new backup_nested_element('module', array('modulename'));
241 $course->add_child($category);
243 $course->add_child($tags);
244 $tags->add_child($tag);
246 $course->add_child($allowedmodules);
247 $allowedmodules->add_child($module);
251 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
252 $courserec->contextid = $this->task->get_contextid();
254 $course->set_source_array(array($courserec));
256 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
258 $category->set_source_array(array($categoryrec));
260 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
262 JOIN {tag_instance} ti ON ti.tagid = t.id
263 WHERE ti.itemtype = ?
264 AND ti.itemid = ?', array(
265 $this->is_sqlparam('course'),
266 backup::VAR_PARENTID));
268 $module->set_source_sql('SELECT m.name AS modulename
270 JOIN {course_allowed_modules} cam ON m.id = cam.module
271 WHERE course = ?', array(backup::VAR_COURSEID));
275 $course->annotate_ids('role', 'defaultrole');
276 $course->annotate_ids('grouping', 'defaultgroupingid');
278 $course->annotate_files(array('course_summary', 'course_content'), null);
280 // Return root element ($course)
287 * structure step that will generate the roles.xml file for the given context, observing
288 * the role_assignments setting to know if that part needs to be included
290 class backup_roles_structure_step extends backup_structure_step {
292 protected function define_structure() {
294 // To know if we are including role assignments
295 $roleassignments = $this->get_setting_value('role_assignments');
297 // Define each element separated
299 $roles = new backup_nested_element('roles');
301 $overrides = new backup_nested_element('role_overrides');
303 $override = new backup_nested_element('override', array('id'), array(
304 'roleid', 'capability', 'permission', 'timemodified',
307 $assignments = new backup_nested_element('role_assignments');
309 $assignment = new backup_nested_element('assignment', array('id'), array(
310 'roleid', 'userid', 'hidden', 'timestart',
311 'timeend', 'timemodified', 'modifierid', 'enrol',
315 $roles->add_child($overrides);
316 $roles->add_child($assignments);
318 $overrides->add_child($override);
319 $assignments->add_child($assignment);
323 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
325 // Assignments only added if specified
326 if ($roleassignments) {
327 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
330 // Define id annotations
331 $override->annotate_ids('role', 'roleid');
333 $assignment->annotate_ids('role', 'roleid');
335 $assignment->annotate_ids('user', 'userid');
342 * structure step that will generate the roles.xml containing the
343 * list of roles used along the whole backup process. Just raw
344 * list of used roles from role table
346 class backup_final_roles_structure_step extends backup_structure_step {
348 protected function define_structure() {
352 $rolesdef = new backup_nested_element('roles_definition');
354 $role = new backup_nested_element('role', array('id'), array(
355 'name', 'shortname', 'nameincourse', 'description',
356 'sortorder', 'archetype'));
360 $rolesdef->add_child($role);
364 $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
366 JOIN {backup_ids_temp} bi ON r.id = bi.itemid
367 LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
368 WHERE bi.backupid = ?
369 AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
371 // Return main element (rolesdef)
377 * structure step that will generate the scales.xml containing the
378 * list of scales used along the whole backup process.
380 class backup_final_scales_structure_step extends backup_structure_step {
382 protected function define_structure() {
386 $scalesdef = new backup_nested_element('scales_definition');
388 $scale = new backup_nested_element('scale', array('id'), array(
389 'courseid', 'userid', 'name', 'scale',
390 'description', 'descriptionformat', 'timemodified'));
394 $scalesdef->add_child($scale);
398 $scale->set_source_sql("SELECT s.*
400 JOIN {backup_ids_temp} bi ON s.id = bi.itemid
401 WHERE bi.backupid = ?
402 AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
404 // Return main element (scalesdef)
410 * structure step that will generate the outcomes.xml containing the
411 * list of outcomes used along the whole backup process.
413 class backup_final_outcomes_structure_step extends backup_structure_step {
415 protected function define_structure() {
419 $outcomesdef = new backup_nested_element('outcomes_definition');
421 $outcome = new backup_nested_element('outcome', array('id'), array(
422 'courseid', 'userid', 'shortname', 'fullname',
423 'scaleid', 'description', 'descriptionformat', 'timecreated',
424 'timemodified','usermodified'));
428 $outcomesdef->add_child($outcome);
432 $outcome->set_source_sql("SELECT o.*
433 FROM {grade_outcomes} o
434 JOIN {backup_ids_temp} bi ON o.id = bi.itemid
435 WHERE bi.backupid = ?
436 AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
438 // Return main element (outcomesdef)
444 * structure step in charge of constructing the filters.xml file for all the filters found
447 class backup_filters_structure_step extends backup_structure_step {
449 protected function define_structure() {
451 // Define each element separated
453 $filters = new backup_nested_element('filters');
455 $actives = new backup_nested_element('filter_actives');
457 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
459 $configs = new backup_nested_element('filter_configs');
461 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
465 $filters->add_child($actives);
466 $filters->add_child($configs);
468 $actives->add_child($active);
469 $configs->add_child($config);
473 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
475 $active->set_source_array($activearr);
476 $config->set_source_array($configarr);
478 // Return the root element (filters)
484 * structure step in charge of constructing the comments.xml file for all the comments found
487 class backup_comments_structure_step extends backup_structure_step {
489 protected function define_structure() {
491 // Define each element separated
493 $comments = new backup_nested_element('comments');
495 $comment = new backup_nested_element('comment', array('id'), array(
496 'commentarea', 'itemid', 'content', 'format',
497 'userid', 'timecreated'));
501 $comments->add_child($comment);
505 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
507 // Define id annotations
509 $comment->annotate_ids('user', 'userid');
511 // Return the root element (comments)
517 * structure step in charge if constructing the completion.xml file for all the users completion
518 * information in a given activity
520 class backup_userscompletion_structure_step extends backup_structure_step {
522 protected function define_structure() {
524 // Define each element separated
526 $completions = new backup_nested_element('completions');
528 $completion = new backup_nested_element('completion', array('id'), array(
529 'userid', 'completionstate', 'viewed', 'timemodified'));
533 $completions->add_child($completion);
537 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
539 // Define id annotations
541 $completion->annotate_ids('user', 'userid');
543 // Return the root element (completions)
549 * structure step in charge of constructing the main groups.xml file for all the groups and
550 * groupings information already annotated
552 class backup_groups_structure_step extends backup_structure_step {
554 protected function define_structure() {
556 // To know if we are including users
557 $users = $this->get_setting_value('users');
559 // Define each element separated
561 $groups = new backup_nested_element('groups');
563 $group = new backup_nested_element('group', array('id'), array(
564 'name', 'description', 'descriptionformat', 'enrolmentkey',
565 'picture', 'hidepicture', 'timecreated', 'timemodified'));
567 $members = new backup_nested_element('group_members');
569 $member = new backup_nested_element('group_member', array('id'), array(
570 'userid', 'timeadded'));
572 $groupings = new backup_nested_element('groupings');
574 $grouping = new backup_nested_element('grouping', 'id', array(
575 'name', 'description', 'descriptionformat', 'configdata',
576 'timecreated', 'timemodified'));
578 $groupinggroups = new backup_nested_element('grouping_groups');
580 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
581 'groupid', 'timeadded'));
585 $groups->add_child($group);
586 $groups->add_child($groupings);
588 $group->add_child($members);
589 $members->add_child($member);
591 $groupings->add_child($grouping);
592 $grouping->add_child($groupinggroups);
593 $groupinggroups->add_child($groupinggroup);
597 $group->set_source_sql("
600 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
601 WHERE bi.backupid = ?
602 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
604 // This only happens if we are including users
606 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
609 $grouping->set_source_sql("
612 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
613 WHERE bi.backupid = ?
614 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
616 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
618 // Define id annotations (as final)
620 $member->annotate_ids('userfinal', 'userid');
622 // Define file annotations
624 // TODO: Change "course_group_image" file area to the one finally used for group images
625 $group->annotate_files(array('course_group_description', 'course_group_image'), 'id');
627 // Return the root element (groups)
633 * structure step in charge of constructing the main users.xml file for all the users already
634 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
637 class backup_users_structure_step extends backup_structure_step {
639 protected function define_structure() {
642 // To know if we are anonymizing users
643 $anonymize = $this->get_setting_value('anonymize');
644 // To know if we are including role assignments
645 $roleassignments = $this->get_setting_value('role_assignments');
647 // Define each element separated
649 $users = new backup_nested_element('users');
651 // Create the array of user fields by hand, as far as we have various bits to control
652 // anonymize option, password backup, mnethostid...
654 // First, the fields not needing anonymization nor special handling
655 $normalfields = array(
656 'confirmed', 'policyagreed', 'deleted',
657 'lang', 'theme', 'timezone', 'firstaccess',
658 'lastaccess', 'lastlogin', 'currentlogin', 'secret',
659 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
660 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
661 'timemodified', 'trustbitmask', 'screenreader');
663 // Then, the fields potentially needing anonymization
665 'username', 'idnumber', 'firstname', 'lastname',
666 'email', 'emailstop', 'lastip', 'picture',
667 'url', 'description', 'description_format', 'imagealt', 'auth');
669 // Add anonymized fields to $userfields with custom final element
670 foreach ($anonfields as $field) {
672 $userfields[] = new anonymizer_final_element($field);
674 $userfields[] = $field; // No anonymization, normally added
678 // mnethosturl requires special handling (custom final element)
679 $userfields[] = new mnethosturl_final_element('mnethosturl');
681 // password added conditionally
682 if (!empty($CFG->includeuserpasswordsinbackup)) {
683 $userfields[] = 'password';
686 // Merge all the fields
687 $userfields = array_merge($userfields, $normalfields);
689 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
691 $customfields = new backup_nested_element('custom_fields');
693 $customfield = new backup_nested_element('custom_field', array('id'), array(
694 'field_name', 'field_type', 'field_data'));
696 $tags = new backup_nested_element('tags');
698 $tag = new backup_nested_element('tag', array('id'), array(
701 $preferences = new backup_nested_element('preferences');
703 $preference = new backup_nested_element('preference', array('id'), array(
706 $roles = new backup_nested_element('roles');
708 $overrides = new backup_nested_element('role_overrides');
710 $override = new backup_nested_element('override', array('id'), array(
711 'roleid', 'capability', 'permission', 'timemodified',
714 $assignments = new backup_nested_element('role_assignments');
716 $assignment = new backup_nested_element('assignment', array('id'), array(
717 'roleid', 'userid', 'hidden', 'timestart',
718 'timeend', 'timemodified', 'modifierid', 'enrol',
723 $users->add_child($user);
725 $user->add_child($customfields);
726 $customfields->add_child($customfield);
728 $user->add_child($tags);
729 $tags->add_child($tag);
731 $user->add_child($preferences);
732 $preferences->add_child($preference);
734 $user->add_child($roles);
736 $roles->add_child($overrides);
737 $roles->add_child($assignments);
739 $overrides->add_child($override);
740 $assignments->add_child($assignment);
744 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
746 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
747 JOIN {context} c ON c.instanceid = u.id
748 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
749 WHERE bi.backupid = ?
751 AND c.contextlevel = ?', array(
752 $this->is_sqlparam($this->get_backupid()),
753 $this->is_sqlparam('userfinal'),
754 $this->is_sqlparam(CONTEXT_USER)));
756 // All the rest on information is only added if we arent
757 // in an anonymized backup
759 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
760 FROM {user_info_field} f
761 JOIN {user_info_data} d ON d.fieldid = f.id
762 WHERE d.userid = ?', array(backup::VAR_PARENTID));
764 $customfield->set_source_alias('shortname', 'field_name');
765 $customfield->set_source_alias('datatype', 'field_type');
766 $customfield->set_source_alias('data', 'field_data');
768 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
770 JOIN {tag_instance} ti ON ti.tagid = t.id
771 WHERE ti.itemtype = ?
772 AND ti.itemid = ?', array(
773 $this->is_sqlparam('user'),
774 backup::VAR_PARENTID));
776 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
778 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
780 // Assignments only added if specified
781 if ($roleassignments) {
782 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
785 // Define id annotations (as final)
786 $override->annotate_ids('rolefinal', 'roleid');
789 // Return root element (users)
795 * structure step in charge of constructing the block.xml file for one
796 * given block (intance and positions). If the block has custom DB structure
797 * that will go to a separate file (different step defined in block class)
799 class backup_block_instance_structure_step extends backup_structure_step {
801 protected function define_structure() {
804 // Define each element separated
806 $block = new backup_nested_element('block', array('id', 'version'), array(
807 'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
808 'defaultregion', 'defaultweight', 'configdata'));
810 $positions = new backup_nested_element('block_positions', null, array(
811 'contextid', 'pagetype', 'subpage', 'visible',
812 'region', 'weight'));
816 $block->add_child($positions);
818 // Transform configdata information if needed (process links and friends)
819 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
820 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
821 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
822 foreach ($configdata as $attribute => $value) {
823 if (in_array($attribute, $attrstotransform)) {
824 $configdata[$attribute] = $this->contenttransformer->process($value);
827 $blockrec->configdata = base64_encode(serialize((object)$configdata));
829 // Get the version of the block
830 $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
834 $block->set_source_array(array($blockrec));
836 $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
838 // Return the root element (block)
844 * structure step in charge of constructing the logs.xml file for all the log records found
847 class backup_activity_logs_structure_step extends backup_structure_step {
849 protected function define_structure() {
851 // Define each element separated
853 $logs = new backup_nested_element('logs');
855 $log = new backup_nested_element('log', array('id'), array(
856 'time', 'userid', 'ip', 'module',
857 'action', 'url', 'info'));
861 $logs->add_child($log);
865 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
868 // NOTE: We don't annotate users from logs as far as they MUST be
869 // always annotated by the activity.
871 // Return the root element (logs)
878 * structure in charge of constructing the inforef.xml file for all the items we want
879 * to have referenced there (users, roles, files...)
881 class backup_inforef_structure_step extends backup_structure_step {
883 protected function define_structure() {
885 // Items we want to include in the inforef file. NOTE: Important to keep this
886 // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
887 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
891 $inforef = new backup_nested_element('inforef');
893 // For each item, conditionally, if there are already records, build element
894 foreach ($items as $itemname) {
895 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
896 $elementroot = new backup_nested_element($itemname . 'ref');
897 $element = new backup_nested_element($itemname, array('id'));
898 $inforef->add_child($elementroot);
899 $elementroot->add_child($element);
900 $element->set_source_sql("
902 FROM {backup_ids_temp}
905 array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
909 // We don't annotate anything there, but rely in the next step
910 // (move_inforef_annotations_to_final) that will change all the
911 // already saved 'inforref' entries to their 'final' annotations.
917 * This step will get all the annotations already processed to inforef.xml file and
918 * transform them into 'final' annotations.
920 class move_inforef_annotations_to_final extends backup_execution_step {
922 protected function define_execution() {
924 // Items we want to include in the inforef file. NOTE: Important to keep this
925 // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
926 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
927 foreach ($items as $itemname) {
929 backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
935 * structure in charge of constructing the files.xml file with all the
936 * annotated (final) files along the process. At, the same time, and
937 * using one specialised nested_element, will copy them form moodle storage
940 class backup_final_files_structure_step extends backup_structure_step {
942 protected function define_structure() {
946 $files = new backup_nested_element('files');
948 $file = new file_nested_element('file', array('id'), array(
949 'contenthash', 'contextid', 'filearea', 'itemid',
950 'filepath', 'filename', 'userid', 'filesize',
951 'mimetype', 'status', 'timecreated', 'timemodified',
952 'source', 'author', 'license'));
956 $files->add_child($file);
960 $file->set_source_sql("SELECT f.*
962 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
963 WHERE bi.backupid = ?
964 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
971 * Structure step in charge of creating the main moodle_backup.xml file
972 * where all the information related to the backup, settings, license and
973 * other information needed on restore is added*/
974 class backup_main_structure_step extends backup_structure_step {
976 protected function define_structure() {
982 $info['name'] = $this->get_setting_value('filename');
983 $info['moodle_version'] = $CFG->version;
984 $info['moodle_release'] = $CFG->release;
985 $info['backup_version'] = $CFG->backup_version;
986 $info['backup_release'] = $CFG->backup_release;
987 $info['backup_date'] = time();
988 $info['backup_uniqueid']= $this->get_backupid();
989 $info['original_wwwroot']=$CFG->wwwroot;
990 $info['original_site_identifier'] = get_site_identifier();
991 $info['original_course_id'] = $this->get_courseid();
993 // Get more information from controller
994 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
998 $moodle_backup = new backup_nested_element('moodle_backup');
1000 $information = new backup_nested_element('information', null, array(
1001 'name', 'moodle_version', 'moodle_release', 'backup_version',
1002 'backup_release', 'backup_date', 'original_wwwroot',
1003 'original_site_identifier', 'original_course_id'));
1005 $details = new backup_nested_element('details');
1007 $detail = new backup_nested_element('detail', array('backup_id'), array(
1008 'type', 'format', 'interactive', 'mode',
1009 'execution', 'executiontime'));
1011 $contents = new backup_nested_element('contents');
1013 $activities = new backup_nested_element('activities');
1015 $activity = new backup_nested_element('activity', null, array(
1016 'moduleid', 'sectionid', 'modulename', 'title',
1019 $sections = new backup_nested_element('sections');
1021 $section = new backup_nested_element('section', null, array(
1022 'sectionid', 'title', 'directory'));
1024 $course = new backup_nested_element('course', null, array(
1025 'courseid', 'title', 'directory'));
1027 $settings = new backup_nested_element('settings');
1029 $setting = new backup_nested_element('setting', null, array(
1030 'level', 'activity', 'name', 'value'));
1034 $moodle_backup->add_child($information);
1036 $information->add_child($details);
1037 $details->add_child($detail);
1039 $information->add_child($contents);
1040 if (!empty($cinfo['activities'])) {
1041 $contents->add_child($activities);
1042 $activities->add_child($activity);
1044 if (!empty($cinfo['sections'])) {
1045 $contents->add_child($sections);
1046 $sections->add_child($section);
1048 if (!empty($cinfo['course'])) {
1049 $contents->add_child($course);
1052 $information->add_child($settings);
1053 $settings->add_child($setting);
1058 $information->set_source_array(array((object)$info));
1060 $detail->set_source_array($dinfo);
1062 $activity->set_source_array($cinfo['activities']);
1064 $section->set_source_array($cinfo['sections']);
1066 $course->set_source_array($cinfo['course']);
1068 $setting->set_source_array($sinfo);
1070 // Prepare some information to be sent to main moodle_backup.xml file
1071 return $moodle_backup;
1077 * Execution step that will generate the final zip file with all the contents
1079 class backup_zip_contents extends backup_execution_step {
1081 protected function define_execution() {
1084 $basepath = $this->get_basepath();
1086 // Get the list of files in directory
1087 $filestemp = get_directory_list($basepath, '', false, true, true);
1089 foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1090 $files[$file] = $basepath . '/' . $file;
1093 // Add the log file if exists
1094 $logfilepath = $basepath . '.log';
1095 if (file_exists($logfilepath)) {
1096 $files['moodle_backup.log'] = $logfilepath;
1099 // Calculate the zip fullpath
1100 $zipfile = $basepath . '/' . $this->get_setting_value('filename');
1102 // Get the zip packer
1103 $zippacker = get_file_packer('application/zip');
1106 $zippacker->archive_to_pathname($files, $zipfile);
1111 * This step will send the generated backup file to its final destination
1113 class backup_store_backup_file extends backup_execution_step {
1115 protected function define_execution() {
1118 $basepath = $this->get_basepath();
1120 // Calculate the zip fullpath
1121 $zipfile = $basepath . '/' . $this->get_setting_value('filename');
1123 // Perform storage and return it (TODO: shouldn't be array but proper result object)
1124 return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
1130 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1131 * and put them to the backup_ids tables, to be used later as base to backup them
1133 class backup_activity_grade_items_to_ids extends backup_execution_step {
1135 protected function define_execution() {
1137 // Fetch all activity grade items
1138 if ($items = grade_item::fetch_all(array(
1139 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1140 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1141 // Annotate them in backup_ids
1142 foreach ($items as $item) {
1143 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1150 * This step will annotate all the groups belonging to already annotated groupings
1152 class backup_annotate_groups_from_groupings extends backup_execution_step {
1154 protected function define_execution() {
1157 // Fetch all the annotated groupings
1158 if ($groupings = $DB->get_records('backup_ids_temp', array(
1159 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1160 foreach ($groupings as $grouping) {
1161 if ($groups = $DB->get_records('groupings_groups', array(
1162 'groupingid' => $grouping->itemid))) {
1163 foreach ($groups as $group) {
1164 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1173 * This step will annotate all the scales belonging to already annotated outcomes
1175 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1177 protected function define_execution() {
1180 // Fetch all the annotated outcomes
1181 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1182 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1183 foreach ($outcomes as $outcome) {
1184 if ($scale = $DB->get_record('grade_outcomes', array(
1185 'id' => $outcome->itemid))) {
1186 // Annotate as scalefinal because it's > 0
1187 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1195 * This step will generate all the user annotations for the already
1196 * annottated (final) users. Need to do this here because each user
1197 * has its own context and structure tasks only are able to handle
1198 * one context. Also, this step will guarantee that every user has
1199 * its context created (req for other steps)
1201 class backup_annotate_all_user_files extends backup_execution_step {
1203 protected function define_execution() {
1206 // List of fileareas we are going to annotate
1207 // TODO: Change "user_image" file area to the one finally used for user images
1209 'user_private', 'user_profile', 'user_image');
1211 // Fetch all annotated (final) users
1212 $rs = $DB->get_recordset('backup_ids_temp', array(
1213 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1214 foreach ($rs as $record) {
1215 $userid = $record->itemid;
1216 $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1217 // Proceed with every user filearea
1218 foreach ($fileareas as $filearea) {
1219 // We don't need to specify itemid ($userid - 4th param) as far as by
1220 // context we can get all the associated files. See MDL-22092
1221 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, $filearea, null);
1229 * structure step in charge of constructing the grades.xml file for all the grade items
1230 * and letters related to one activity
1232 class backup_activity_grades_structure_step extends backup_structure_step {
1234 protected function define_structure() {
1236 // To know if we are including userinfo
1237 $userinfo = $this->get_setting_value('userinfo');
1239 // Define each element separated
1241 $book = new backup_nested_element('activity_gradebook');
1243 $items = new backup_nested_element('grade_items');
1245 $item = new backup_nested_element('grade_item', array('id'), array(
1246 'categoryid', 'itemname', 'itemtype', 'itemmodule',
1247 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1248 'calculation', 'gradetype', 'grademax', 'grademin',
1249 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1250 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1251 'decimals', 'hidden', 'locked', 'locktime',
1252 'needsupdate', 'timecreated', 'timemodified'));
1254 $grades = new backup_nested_element('grade_grades');
1256 $grade = new backup_nested_element('grade_grade', array('id'), array(
1257 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1258 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1259 'locked', 'locktime', 'exported', 'overridden',
1260 'excluded', 'feedback', 'feedbackformat', 'information',
1261 'informationformat', 'timecreated', 'timemodified'));
1263 $letters = new backup_nested_element('grade_letters');
1265 $letter = new backup_nested_element('grade_letter', 'id', array(
1266 'lowerboundary', 'letter'));
1270 $book->add_child($items);
1271 $items->add_child($item);
1273 $item->add_child($grades);
1274 $grades->add_child($grade);
1276 $book->add_child($letters);
1277 $letters->add_child($letter);
1281 $item->set_source_sql("
1283 FROM {grade_items} gi
1284 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1285 WHERE bi.backupid = ?
1286 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1288 // This only happens if we are including user info
1290 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1293 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1297 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1298 $item->annotate_ids('outcome', 'outcomeid');
1300 $grade->annotate_ids('user', 'userid');
1301 $grade->annotate_ids('user', 'usermodified');
1303 // Return the root element (book)