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
28 class create_and_clean_temp_stuff extends backup_execution_step {
30 protected function define_execution() {
31 backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
32 backup_helper::clear_backup_dir($this->get_backupid()); // Empty temp dir, just in case
33 backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
34 backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
39 * Create the directory where all the task (activity/block...) information will be stored
41 class create_taskbasepath_directory extends backup_execution_step {
43 protected function define_execution() {
45 $basepath = $this->task->get_taskbasepath();
46 if (!check_dir_exists($basepath, true, true)) {
47 throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
53 * Abtract tructure step, parent of all the activity structure steps. Used to wrap the
54 * activity structure definition within the main <activity ...> tag
56 abstract class backup_activity_structure_step extends backup_structure_step {
58 protected function prepare_activity_structure($activitystructure) {
60 // Create the wrap element
61 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
64 $activity->add_child($activitystructure);
67 $activityarr = array((object)array(
68 'id' => $this->task->get_activityid(),
69 'moduleid' => $this->task->get_moduleid(),
70 'modulename' => $this->task->get_modulename(),
71 'contextid' => $this->task->get_contextid()));
73 $activity->set_source_array($activityarr);
75 // Return the root element (activity)
81 * Abtract structure step, parent of all the block structure steps. Used to wrap the
82 * block structure definition within the main <block ...> tag
84 abstract class backup_block_structure_step extends backup_structure_step {
86 protected function prepare_block_structure($blockstructure) {
88 // Create the wrap element
89 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
92 $block->add_child($blockstructure);
95 $blockarr = array((object)array(
96 'id' => $this->task->get_blockid(),
97 'blockname' => $this->task->get_blockname(),
98 'contextid' => $this->task->get_contextid()));
100 $block->set_source_array($blockarr);
102 // Return the root element (block)
108 * structure step that will generate the module.xml file for the activity,
109 * acummulating various information about the activity, annotating groupings
110 * and completion/avail conf
112 class backup_module_structure_step extends backup_structure_step {
114 protected function define_structure() {
116 // Define each element separated
118 $module = new backup_nested_element('module', array('id', 'version'), array(
119 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
120 'added', 'score', 'indent', 'visible',
121 'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
122 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
123 'availablefrom', 'availableuntil', 'showavailability'));
125 $availinfo = new backup_nested_element('availability_info');
126 $availability = new backup_nested_element('availability', array('id'), array(
127 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
130 $module->add_child($availinfo);
131 $availinfo->add_child($availability);
135 $module->set_source_sql('
136 SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
137 FROM {course_modules} cm
138 JOIN {modules} m ON m.id = cm.module
139 JOIN {course_sections} s ON s.id = cm.section
140 WHERE cm.id = ?', array(backup::VAR_MODID));
142 $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
144 // Define annotations
145 $module->annotate_ids('grouping', 'groupingid');
147 // Return the root element ($module)
153 * structure step that will genereate the section.xml file for the section
156 class backup_section_structure_step extends backup_structure_step {
158 protected function define_structure() {
160 // Define each element separated
162 $section = new backup_nested_element('section', array('id'), array(
163 'number', 'summary', 'sequence', 'visible'));
167 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
170 $section->annotate_files(array('course_section'), 'id');
177 * structure step that will generate the course.xml file for the course, including
178 * course category reference, tags, metacourse, modules restriction information
179 * and some annotations (files & groupings)
181 class backup_course_structure_step extends backup_structure_step {
183 protected function define_structure() {
186 // Define each element separated
188 $course = new backup_nested_element('course', array('id', 'contextid'), array(
189 'shortname', 'fullname', 'idnumber', 'password',
190 'summary', 'summaryformat', 'format', 'showgrades',
191 'newsitems', 'guest', 'startdate', 'enrolperiod',
192 'numsections', 'marker', 'maxbytes', 'showreports',
193 'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
194 'defaultgroupingid', 'lang', 'theme', 'cost',
195 'currency', 'timecreated', 'timemodified', 'metacourse',
196 'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
197 'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
198 'enrol', 'defaultrole', 'enablecompletion'));
200 $category = new backup_nested_element('category', array('id'), array(
201 'name', 'description'));
203 $tags = new backup_nested_element('tags');
205 $tag = new backup_nested_element('tag', array('id'), array(
208 $allowedmodules = new backup_nested_element('allowed_modules');
210 $module = new backup_nested_element('module', array('modulename'));
214 $course->add_child($category);
216 $course->add_child($tags);
217 $tags->add_child($tag);
219 $course->add_child($allowedmodules);
220 $allowedmodules->add_child($module);
224 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
225 $courserec->contextid = $this->task->get_contextid();
227 $course->set_source_array(array($courserec));
229 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
231 $category->set_source_array(array($categoryrec));
233 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
235 JOIN {tag_instance} ti ON ti.tagid = t.id
236 WHERE ti.itemtype = ?
237 AND ti.itemid = ?', array(
238 $this->is_sqlparam('course'),
239 backup::VAR_PARENTID));
241 $module->set_source_sql('SELECT m.name AS modulename
243 JOIN {course_allowed_modules} cam ON m.id = cam.module
244 WHERE course = ?', array(backup::VAR_COURSEID));
248 $course->annotate_ids('role', 'defaultrole');
249 $course->annotate_ids('grouping', 'defaultgroupingid');
251 $course->annotate_files(array('course_summary', 'course_content'), null);
253 // Return root element ($course)
260 * structure step that will generate the roles.xml file for the given context, observing
261 * the role_assignments setting to know if that part needs to be included
263 class backup_roles_structure_step extends backup_structure_step {
265 protected function define_structure() {
267 // To know if we are including role assignments
268 $roleassignments = $this->get_setting_value('role_assignments');
270 // Define each element separated
272 $roles = new backup_nested_element('roles');
274 $overrides = new backup_nested_element('role_overrides');
276 $override = new backup_nested_element('override', array('id'), array(
277 'roleid', 'capability', 'permission', 'timemodified',
280 $assignments = new backup_nested_element('role_assignments');
282 $assignment = new backup_nested_element('assignment', array('id'), array(
283 'roleid', 'userid', 'hidden', 'timestart',
284 'timeend', 'timemodified', 'modifierid', 'enrol',
288 $roles->add_child($overrides);
289 $roles->add_child($assignments);
291 $overrides->add_child($override);
292 $assignments->add_child($assignment);
296 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
298 // Assignments only added if specified
299 if ($roleassignments) {
300 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
303 // Define id annotations
304 $override->annotate_ids('role', 'roleid');
306 $assignment->annotate_ids('role', 'roleid');
308 $assignment->annotate_ids('user', 'userid');
315 * structure step that will generate the roles.xml containing the
316 * list of roles used along the whole backup process. Just raw
317 * list of used roles from role table
319 class backup_final_roles_structure_step extends backup_structure_step {
321 protected function define_structure() {
325 $rolesdef = new backup_nested_element('roles_definition');
327 $role = new backup_nested_element('role', array('id'), array(
328 'name', 'shortname', 'nameincourse', 'description',
329 'sortorder', 'archetype'));
333 $rolesdef->add_child($role);
337 $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
339 JOIN {backup_ids_temp} bi ON r.id = bi.itemid
340 LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
341 WHERE bi.backupid = ?
342 AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
344 // Return main element (rolesdef)
350 * structure step that will generate the scales.xml containing the
351 * list of scales used along the whole backup process.
353 class backup_final_scales_structure_step extends backup_structure_step {
355 protected function define_structure() {
359 $scalesdef = new backup_nested_element('scales_definition');
361 $scale = new backup_nested_element('scale', array('id'), array(
362 'courseid', 'userid', 'name', 'scale',
363 'description', 'descriptionformat', 'timemodified'));
367 $scalesdef->add_child($scale);
371 $scale->set_source_sql("SELECT s.*
373 JOIN {backup_ids_temp} bi ON s.id = bi.itemid
374 WHERE bi.backupid = ?
375 AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
377 // Return main element (scalesdef)
383 * structure step that will generate the outcomes.xml containing the
384 * list of outcomes used along the whole backup process.
386 class backup_final_outcomes_structure_step extends backup_structure_step {
388 protected function define_structure() {
392 $outcomesdef = new backup_nested_element('outcomes_definition');
394 $outcome = new backup_nested_element('outcome', array('id'), array(
395 'courseid', 'userid', 'shortname', 'fullname',
396 'scaleid', 'description', 'descriptionformat', 'timecreated',
397 'timemodified','usermodified'));
401 $outcomesdef->add_child($outcome);
405 $outcome->set_source_sql("SELECT o.*
406 FROM {grade_outcomes} o
407 JOIN {backup_ids_temp} bi ON o.id = bi.itemid
408 WHERE bi.backupid = ?
409 AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
411 // Return main element (outcomesdef)
417 * structure step in charge of constructing the filters.xml file for all the filters found
420 class backup_filters_structure_step extends backup_structure_step {
422 protected function define_structure() {
424 // Define each element separated
426 $filters = new backup_nested_element('filters');
428 $actives = new backup_nested_element('filter_actives');
430 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
432 $configs = new backup_nested_element('filter_configs');
434 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
438 $filters->add_child($actives);
439 $filters->add_child($configs);
441 $actives->add_child($active);
442 $configs->add_child($config);
446 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
448 $active->set_source_array($activearr);
449 $config->set_source_array($configarr);
451 // Return the root element (filters)
457 * structure step in charge of constructing the comments.xml file for all the comments found
460 class backup_comments_structure_step extends backup_structure_step {
462 protected function define_structure() {
464 // Define each element separated
466 $comments = new backup_nested_element('comments');
468 $comment = new backup_nested_element('comment', array('id'), array(
469 'commentarea', 'itemid', 'content', 'format',
470 'userid', 'timecreated'));
474 $comments->add_child($comment);
478 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
480 // Define id annotations
482 $comment->annotate_ids('user', 'userid');
484 // Return the root element (comments)
490 * structure step in charge if constructing the completion.xml file for all the users completion
491 * information in a given activity
493 class backup_userscompletion_structure_step extends backup_structure_step {
495 protected function define_structure() {
497 // Define each element separated
499 $completions = new backup_nested_element('completions');
501 $completion = new backup_nested_element('completion', array('id'), array(
502 'userid', 'completionstate', 'viewed', 'timemodified'));
506 $completions->add_child($completion);
510 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
512 // Define id annotations
514 $completion->annotate_ids('user', 'userid');
516 // Return the root element (completions)
522 * structure step in charge of constructing the main groups.xml file for all the groups and
523 * groupings information already annotated
525 class backup_groups_structure_step extends backup_structure_step {
527 protected function define_structure() {
529 // To know if we are including users
530 $users = $this->get_setting_value('users');
532 // Define each element separated
534 $groups = new backup_nested_element('groups');
536 $group = new backup_nested_element('group', array('id'), array(
537 'name', 'description', 'descriptionformat', 'enrolmentkey',
538 'picture', 'hidepicture', 'timecreated', 'timemodified'));
540 $members = new backup_nested_element('group_members');
542 $member = new backup_nested_element('group_member', array('id'), array(
543 'userid', 'timeadded'));
545 $groupings = new backup_nested_element('groupings');
547 $grouping = new backup_nested_element('grouping', 'id', array(
548 'name', 'description', 'descriptionformat', 'configdata',
549 'timecreated', 'timemodified'));
551 $groupinggroups = new backup_nested_element('grouping_groups');
553 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
554 'groupid', 'timeadded'));
558 $groups->add_child($group);
559 $groups->add_child($groupings);
561 $group->add_child($members);
562 $members->add_child($member);
564 $groupings->add_child($grouping);
565 $grouping->add_child($groupinggroups);
566 $groupinggroups->add_child($groupinggroup);
570 $group->set_source_sql("
573 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
574 WHERE bi.backupid = ?
575 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
577 // This only happens if we are including users
579 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
582 $grouping->set_source_sql("
585 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
586 WHERE bi.backupid = ?
587 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
589 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
591 // Define id annotations (as final)
593 $member->annotate_ids('userfinal', 'userid');
595 // Define file annotations
597 // TODO: Change "course_group_image" file area to the one finally used for group images
598 $group->annotate_files(array('course_group_description', 'course_group_image'), 'id');
600 // Return the root element (groups)
606 * structure step in charge of constructing the main users.xml file for all the users already
607 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
610 class backup_users_structure_step extends backup_structure_step {
612 protected function define_structure() {
615 // To know if we are anonymizing users
616 $anonymize = $this->get_setting_value('anonymize');
617 // To know if we are including role assignments
618 $roleassignments = $this->get_setting_value('role_assignments');
620 // Define each element separated
622 $users = new backup_nested_element('users');
624 // Create the array of user fields by hand, as far as we have various bits to control
625 // anonymize option, password backup, mnethostid...
627 // First, the fields not needing anonymization nor special handling
628 $normalfields = array(
629 'confirmed', 'policyagreed', 'deleted',
630 'lang', 'theme', 'timezone', 'firstaccess',
631 'lastaccess', 'lastlogin', 'currentlogin', 'secret',
632 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
633 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
634 'timemodified', 'trustbitmask', 'screenreader');
636 // Then, the fields potentially needing anonymization
638 'username', 'idnumber', 'firstname', 'lastname',
639 'email', 'emailstop', 'lastip', 'picture',
640 'url', 'description', 'description_format', 'imagealt', 'auth');
642 // Add anonymized fields to $userfields with custom final element
643 foreach ($anonfields as $field) {
645 $userfields[] = new anonymizer_final_element($field);
647 $userfields[] = $field; // No anonymization, normally added
651 // mnethosturl requires special handling (custom final element)
652 $userfields[] = new mnethosturl_final_element('mnethosturl');
654 // password added conditionally
655 if (!empty($CFG->includeuserpasswordsinbackup)) {
656 $userfields[] = 'password';
659 // Merge all the fields
660 $userfields = array_merge($userfields, $normalfields);
662 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
664 $customfields = new backup_nested_element('custom_fields');
666 $customfield = new backup_nested_element('custom_field', array('id'), array(
667 'field_name', 'field_type', 'field_data'));
669 $tags = new backup_nested_element('tags');
671 $tag = new backup_nested_element('tag', array('id'), array(
674 $preferences = new backup_nested_element('preferences');
676 $preference = new backup_nested_element('preference', array('id'), array(
679 $roles = new backup_nested_element('roles');
681 $overrides = new backup_nested_element('role_overrides');
683 $override = new backup_nested_element('override', array('id'), array(
684 'roleid', 'capability', 'permission', 'timemodified',
687 $assignments = new backup_nested_element('role_assignments');
689 $assignment = new backup_nested_element('assignment', array('id'), array(
690 'roleid', 'userid', 'hidden', 'timestart',
691 'timeend', 'timemodified', 'modifierid', 'enrol',
696 $users->add_child($user);
698 $user->add_child($customfields);
699 $customfields->add_child($customfield);
701 $user->add_child($tags);
702 $tags->add_child($tag);
704 $user->add_child($preferences);
705 $preferences->add_child($preference);
707 $user->add_child($roles);
709 $roles->add_child($overrides);
710 $roles->add_child($assignments);
712 $overrides->add_child($override);
713 $assignments->add_child($assignment);
717 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
719 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
720 JOIN {context} c ON c.instanceid = u.id
721 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
722 WHERE bi.backupid = ?
724 AND c.contextlevel = ?', array(
725 $this->is_sqlparam($this->get_backupid()),
726 $this->is_sqlparam('userfinal'),
727 $this->is_sqlparam(CONTEXT_USER)));
729 // All the rest on information is only added if we arent
730 // in an anonymized backup
732 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
733 FROM {user_info_field} f
734 JOIN {user_info_data} d ON d.fieldid = f.id
735 WHERE d.userid = ?', array(backup::VAR_PARENTID));
737 $customfield->set_source_alias('shortname', 'field_name');
738 $customfield->set_source_alias('datatype', 'field_type');
739 $customfield->set_source_alias('data', 'field_data');
741 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
743 JOIN {tag_instance} ti ON ti.tagid = t.id
744 WHERE ti.itemtype = ?
745 AND ti.itemid = ?', array(
746 $this->is_sqlparam('user'),
747 backup::VAR_PARENTID));
749 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
751 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
753 // Assignments only added if specified
754 if ($roleassignments) {
755 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
758 // Define id annotations (as final)
759 $override->annotate_ids('rolefinal', 'roleid');
762 // Return root element (users)
768 * structure step in charge of constructing the block.xml file for one
769 * given block (intance and positions). If the block has custom DB structure
770 * that will go to a separate file (different step defined in block class)
772 class backup_block_instance_structure_step extends backup_structure_step {
774 protected function define_structure() {
777 // Define each element separated
779 $block = new backup_nested_element('block', array('id', 'version'), array(
780 'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
781 'defaultregion', 'defaultweight', 'configdata'));
783 $positions = new backup_nested_element('block_positions', null, array(
784 'contextid', 'pagetype', 'subpage', 'visible',
785 'region', 'weight'));
789 $block->add_child($positions);
791 // Transform configdata information if needed (process links and friends)
792 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
793 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
794 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
795 foreach ($configdata as $attribute => $value) {
796 if (in_array($attribute, $attrstotransform)) {
797 $configdata[$attribute] = $this->contenttransformer->process($value);
800 $blockrec->configdata = base64_encode(serialize((object)$configdata));
802 // Get the version of the block
803 $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
807 $block->set_source_array(array($blockrec));
809 $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
811 // Return the root element (block)
817 * structure step in charge of constructing the logs.xml file for all the log records found
820 class backup_activity_logs_structure_step extends backup_structure_step {
822 protected function define_structure() {
824 // Define each element separated
826 $logs = new backup_nested_element('logs');
828 $log = new backup_nested_element('log', array('id'), array(
829 'time', 'userid', 'ip', 'module',
830 'action', 'url', 'info'));
834 $logs->add_child($log);
838 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
841 // NOTE: We don't annotate users from logs as far as they MUST be
842 // always annotated by the activity.
844 // Return the root element (logs)
851 * structure in charge of constructing the inforef.xml file for all the items we want
852 * to have referenced there (users, roles, files...)
854 class backup_inforef_structure_step extends backup_structure_step {
856 protected function define_structure() {
858 // Items we want to include in the inforef file. NOTE: Important to keep this
859 // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
860 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
864 $inforef = new backup_nested_element('inforef');
866 // For each item, conditionally, if there are already records, build element
867 foreach ($items as $itemname) {
868 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
869 $elementroot = new backup_nested_element($itemname . 'ref');
870 $element = new backup_nested_element($itemname, array('id'));
871 $inforef->add_child($elementroot);
872 $elementroot->add_child($element);
873 $element->set_source_sql("
875 FROM {backup_ids_temp}
878 array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
882 // We don't annotate anything there, but rely in the next step
883 // (move_inforef_annotations_to_final) that will change all the
884 // already saved 'inforref' entries to their 'final' annotations.
890 * This step will get all the annotations already processed to inforef.xml file and
891 * transform them into 'final' annotations.
893 class move_inforef_annotations_to_final extends backup_execution_step {
895 protected function define_execution() {
897 // Items we want to include in the inforef file. NOTE: Important to keep this
898 // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
899 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
900 foreach ($items as $itemname) {
902 backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
908 * structure in charge of constructing the files.xml file with all the
909 * annotated (final) files along the process. At, the same time, and
910 * using one specialised nested_element, will copy them form moodle storage
913 class backup_final_files_structure_step extends backup_structure_step {
915 protected function define_structure() {
919 $files = new backup_nested_element('files');
921 $file = new file_nested_element('file', array('id'), array(
922 'contenthash', 'contextid', 'filearea', 'itemid',
923 'filepath', 'filename', 'userid', 'filesize',
924 'mimetype', 'status', 'timecreated', 'timemodified',
925 'source', 'author', 'license'));
929 $files->add_child($file);
933 $file->set_source_sql("SELECT f.*
935 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
936 WHERE bi.backupid = ?
937 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
944 * Structure step in charge of creating the main moodle_backup.xml file
945 * where all the information related to the backup, settings, license and
946 * other information needed on restore is added*/
947 class backup_main_structure_step extends backup_structure_step {
949 protected function define_structure() {
955 $info['name'] = $this->get_setting_value('filename');
956 $info['moodle_version'] = $CFG->version;
957 $info['moodle_release'] = $CFG->release;
958 $info['backup_version'] = $CFG->backup_version;
959 $info['backup_release'] = $CFG->backup_release;
960 $info['backup_date'] = time();
961 $info['backup_uniqueid']= $this->get_backupid();
962 $info['original_wwwroot']=$CFG->wwwroot;
963 $info['original_site_identifier'] = get_site_identifier();
964 $info['original_course_id'] = $this->get_courseid();
966 // Get more information from controller
967 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
971 $moodle_backup = new backup_nested_element('moodle_backup');
973 $information = new backup_nested_element('information', null, array(
974 'name', 'moodle_version', 'moodle_release', 'backup_version',
975 'backup_release', 'backup_date', 'original_wwwroot',
976 'original_site_identifier', 'original_course_id'));
978 $details = new backup_nested_element('details');
980 $detail = new backup_nested_element('detail', array('backup_id'), array(
981 'type', 'format', 'interactive', 'mode',
982 'execution', 'executiontime'));
984 $contents = new backup_nested_element('contents');
986 $activities = new backup_nested_element('activities');
988 $activity = new backup_nested_element('activity', null, array(
989 'moduleid', 'sectionid', 'modulename', 'title',
992 $sections = new backup_nested_element('sections');
994 $section = new backup_nested_element('section', null, array(
995 'sectionid', 'title', 'directory'));
997 $course = new backup_nested_element('course', null, array(
998 'courseid', 'title', 'directory'));
1000 $settings = new backup_nested_element('settings');
1002 $setting = new backup_nested_element('setting', null, array(
1003 'level', 'activity', 'name', 'value'));
1007 $moodle_backup->add_child($information);
1009 $information->add_child($details);
1010 $details->add_child($detail);
1012 $information->add_child($contents);
1013 if (!empty($cinfo['activities'])) {
1014 $contents->add_child($activities);
1015 $activities->add_child($activity);
1017 if (!empty($cinfo['sections'])) {
1018 $contents->add_child($sections);
1019 $sections->add_child($section);
1021 if (!empty($cinfo['course'])) {
1022 $contents->add_child($course);
1025 $information->add_child($settings);
1026 $settings->add_child($setting);
1031 $information->set_source_array(array((object)$info));
1033 $detail->set_source_array($dinfo);
1035 $activity->set_source_array($cinfo['activities']);
1037 $section->set_source_array($cinfo['sections']);
1039 $course->set_source_array($cinfo['course']);
1041 $setting->set_source_array($sinfo);
1043 // Prepare some information to be sent to main moodle_backup.xml file
1044 return $moodle_backup;
1050 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1051 * and put them to the backup_ids tables, to be used later as base to backup them
1053 class backup_activity_grade_items_to_ids extends backup_execution_step {
1055 protected function define_execution() {
1057 // Fetch all activity grade items
1058 if ($items = grade_item::fetch_all(array(
1059 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1060 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1061 // Annotate them in backup_ids
1062 foreach ($items as $item) {
1063 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1070 * This step will annotate all the groups belonging to already annotated groupings
1072 class backup_annotate_groups_from_groupings extends backup_execution_step {
1074 protected function define_execution() {
1077 // Fetch all the annotated groupings
1078 if ($groupings = $DB->get_records('backup_ids_temp', array(
1079 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1080 foreach ($groupings as $grouping) {
1081 if ($groups = $DB->get_records('groupings_groups', array(
1082 'groupingid' => $grouping->itemid))) {
1083 foreach ($groups as $group) {
1084 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1093 * This step will annotate all the scales belonging to already annotated outcomes
1095 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1097 protected function define_execution() {
1100 // Fetch all the annotated outcomes
1101 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1102 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1103 foreach ($outcomes as $outcome) {
1104 if ($scale = $DB->get_record('grade_outcomes', array(
1105 'id' => $outcome->itemid))) {
1106 // Annotate as scalefinal because it's > 0
1107 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1115 * This step will generate all the user annotations for the already
1116 * annottated (final) users. Need to do this here because each user
1117 * has its own context and structure tasks only are able to handle
1118 * one context. Also, this step will guarantee that every user has
1119 * its context created (req for other steps)
1121 class backup_annotate_all_user_files extends backup_execution_step {
1123 protected function define_execution() {
1126 // List of fileareas we are going to annotate
1127 // TODO: Change "user_image" file area to the one finally used for user images
1129 'user_private', 'user_profile', 'user_image');
1131 // Fetch all annotated (final) users
1132 $rs = $DB->get_recordset('backup_ids_temp', array(
1133 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1134 foreach ($rs as $record) {
1135 $userid = $record->itemid;
1136 $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1137 // Proceed with every user filearea
1138 foreach ($fileareas as $filearea) {
1139 // We don't need to specify itemid ($userid - 4th param) as far as by
1140 // context we can get all the associated files. See MDL-22092
1141 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, $filearea, null);
1149 * structure step in charge of constructing the grades.xml file for all the grade items
1150 * and letters related to one activity
1152 class backup_activity_grades_structure_step extends backup_structure_step {
1154 protected function define_structure() {
1156 // To know if we are including userinfo
1157 $userinfo = $this->get_setting_value('userinfo');
1159 // Define each element separated
1161 $book = new backup_nested_element('activity_gradebook');
1163 $items = new backup_nested_element('grade_items');
1165 $item = new backup_nested_element('grade_item', array('id'), array(
1166 'categoryid', 'itemname', 'itemtype', 'itemmodule',
1167 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1168 'calculation', 'gradetype', 'grademax', 'grademin',
1169 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1170 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1171 'decimals', 'hidden', 'locked', 'locktime',
1172 'needsupdate', 'timecreated', 'timemodified'));
1174 $grades = new backup_nested_element('grade_grades');
1176 $grade = new backup_nested_element('grade_grade', array('id'), array(
1177 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1178 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1179 'locked', 'locktime', 'exported', 'overridden',
1180 'excluded', 'feedback', 'feedbackformat', 'information',
1181 'informationformat', 'timecreated', 'timemodified'));
1183 $letters = new backup_nested_element('grade_letters');
1185 $letter = new backup_nested_element('grade_letter', 'id', array(
1186 'lowerboundary', 'letter'));
1190 $book->add_child($items);
1191 $items->add_child($item);
1193 $item->add_child($grades);
1194 $grades->add_child($grade);
1196 $book->add_child($letters);
1197 $letters->add_child($letter);
1201 $item->set_source_sql("
1203 FROM {grade_items} gi
1204 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1205 WHERE bi.backupid = ?
1206 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1208 // This only happens if we are including user info
1210 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1213 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1217 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1218 $item->annotate_ids('outcome', 'outcomeid');
1220 $grade->annotate_ids('user', 'userid');
1221 $grade->annotate_ids('user', 'usermodified');
1223 // Return the root element (book)