422a79fb443bdb409608dfbd4f69e22c05cea95e
[moodle.git] / backup / moodle2 / backup_stepslib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package moodlecore
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
23  */
25 /**
26  * Define all the backup steps that will be used by common tasks in backup
27  */
29 /**
30  * create the temp dir where backup/restore will happen,
31  * delete old directories and create temp ids table
32  */
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
40     }
41 }
43 /**
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)
49  */
50 class drop_and_clean_temp_stuff extends backup_execution_step {
52     protected function define_execution() {
53         global $CFG;
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
58         }
59     }
60 }
62 /**
63  * Create the directory where all the task (activity/block...) information will be stored
64  */
65 class create_taskbasepath_directory extends backup_execution_step {
67     protected function define_execution() {
68         global $CFG;
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);
72         }
73     }
74 }
76 /**
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)
80  */
81 abstract class backup_activity_structure_step extends backup_structure_step {
83     protected function add_subplugin_structure($subpluginname, $element, $multiple) {
85         global $CFG;
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());
91         }
92         include($subpluginsfile);
93         if (!array_key_exists($subpluginname, $subplugins)) {
94              throw new backup_step_exception('incorrect_subplugin_type', $subpluginname);
95         }
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
102         $elements = array();
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);
113                 }
114             }
115         }
116         // Finished, add optigroup to element
117         $element->add_child($optigroup);
118     }
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);
125         // Build the tree
126         $activity->add_child($activitystructure);
128         // Set the source
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)
138         return $activity;
139     }
142 /**
143  * Abtract structure step, parent of all the block structure steps. Used to wrap the
144  * block structure definition within the main <block ...> tag
145  */
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);
153         // Build the tree
154         $block->add_child($blockstructure);
156         // Set the source
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)
165         return $block;
166     }
169 /**
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
173  */
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'));
191         // Define the tree
192         $module->add_child($availinfo);
193         $availinfo->add_child($availability);
195         // Set the sources
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)
210         return $module;
211     }
214 /**
215  * structure step that will genereate the section.xml file for the section
216  * annotating files
217  */
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'));
227         // Define sources
229         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
231         // Aliases
232         $section->set_source_alias('section', 'number');
234         // Set annotations
235         $section->annotate_files('course', 'section', 'id');
237         return $section;
238     }
241 /**
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)
245  */
246 class backup_course_structure_step extends backup_structure_step {
248     protected function define_structure() {
249         global $DB;
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(
270             'name', 'rawname'));
272         $allowedmodules = new backup_nested_element('allowed_modules');
274         $module = new backup_nested_element('module', array('modulename'));
276         // Build the tree
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);
286         // Set the sources
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
298                                 FROM {tag} t
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
306                                    FROM {modules} m
307                                    JOIN {course_allowed_modules} cam ON m.id = cam.module
308                                   WHERE course = ?', array(backup::VAR_COURSEID));
310         // Some annotations
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)
319         return $course;
320     }
323 /**
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
326  */
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',
342             'modifierid'));
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
348             'sortorder'));
350         // Build the tree
351         $roles->add_child($overrides);
352         $roles->add_child($assignments);
354         $overrides->add_child($override);
355         $assignments->add_child($assignment);
357         // Define sources
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));
364         }
366         // Define id annotations
367         $override->annotate_ids('role', 'roleid');
369         $assignment->annotate_ids('role', 'roleid');
371         $assignment->annotate_ids('user', 'userid');
373         return $roles;
374     }
377 /**
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
381  */
382 class backup_final_roles_structure_step extends backup_structure_step {
384     protected function define_structure() {
386         // Define elements
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'));
394         // Build the tree
396         $rolesdef->add_child($role);
398         // Define sources
400         $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
401                                  FROM {role} r
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)
408         return $rolesdef;
409     }
412 /**
413  * structure step that will generate the scales.xml containing the
414  * list of scales used along the whole backup process.
415  */
416 class backup_final_scales_structure_step extends backup_structure_step {
418     protected function define_structure() {
420         // Define elements
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'));
428         // Build the tree
430         $scalesdef->add_child($scale);
432         // Define sources
434         $scale->set_source_sql("SELECT s.*
435                                   FROM {scale} 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)
441         return $scalesdef;
442     }
445 /**
446  * structure step that will generate the outcomes.xml containing the
447  * list of outcomes used along the whole backup process.
448  */
449 class backup_final_outcomes_structure_step extends backup_structure_step {
451     protected function define_structure() {
453         // Define elements
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'));
462         // Build the tree
464         $outcomesdef->add_child($outcome);
466         // Define sources
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)
475         return $outcomesdef;
476     }
479 /**
480  * structure step in charge of constructing the filters.xml file for all the filters found
481  * in activity
482  */
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'));
499         // Build the tree
501         $filters->add_child($actives);
502         $filters->add_child($configs);
504         $actives->add_child($active);
505         $configs->add_child($config);
507         // Define sources
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)
515         return $filters;
516     }
519 /**
520  * structure step in charge of constructing the comments.xml file for all the comments found
521  * in a given context
522  */
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'));
535         // Build the tree
537         $comments->add_child($comment);
539         // Define sources
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)
548         return $comments;
549     }
552 /**
553  * structure step in charge if constructing the completion.xml file for all the users completion
554  * information in a given activity
555  */
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'));
567         // Build the tree
569         $completions->add_child($completion);
571         // Define sources
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)
580         return $completions;
581     }
584 /**
585  * structure step in charge of constructing the main groups.xml file for all the groups and
586  * groupings information already annotated
587  */
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'));
619         // Build the tree
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);
631         // Define sources
633         $group->set_source_sql("
634             SELECT g.*
635               FROM {groups} g
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
641         if ($users) {
642             $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
643         }
645         $grouping->set_source_sql("
646             SELECT g.*
647               FROM {groupings} g
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)
665         return $groups;
666     }
669 /**
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
672  * overrides.
673  */
674 class backup_users_structure_step extends backup_structure_step {
676     protected function define_structure() {
677         global $CFG;
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
701         $anonfields = array(
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) {
708             if ($anonymize) {
709                 $userfields[] = new anonymizer_final_element($field);
710             } else {
711                 $userfields[] = $field; // No anonymization, normally added
712             }
713         }
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';
721         }
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(
736             'name', 'rawname'));
738         $preferences = new backup_nested_element('preferences');
740         $preference = new backup_nested_element('preference', array('id'), array(
741             'name', 'value'));
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',
749             'modifierid'));
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
755             'sortorder'));
757         // Build the tree
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);
778         // Define sources
780         $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
781                                  FROM {user} u
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 = ?
786                                   AND bi.itemname = ?
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
794         if (!$anonymize) {
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
805                                     FROM {tag} t
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'));
819             }
821             // Define id annotations (as final)
822             $override->annotate_ids('rolefinal', 'roleid');
823         }
825         // Return root element (users)
826         return $users;
827     }
830 /**
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)
834  */
835 class backup_block_instance_structure_step extends backup_structure_step {
837     protected function define_structure() {
838         global $DB;
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'));
850         // Build the tree
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);
861                 }
862             }
863             $blockrec->configdata = base64_encode(serialize((object)$configdata));
864         }
865         // Get the version of the block
866         $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
868         // Define sources
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)
875         return $block;
876     }
879 /**
880  * structure step in charge of constructing the logs.xml file for all the log records found
881  * in activity
882  */
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'));
895         // Build the tree
897         $logs->add_child($log);
899         // Define sources
901         $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
903         // Annotations
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)
909         return $logs;
910     }
913 /**
914  * structure in charge of constructing the inforef.xml file for all the items we want
915  * to have referenced there (users, roles, files...)
916  */
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');
925         // Build the tree
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("
937                     SELECT itemid AS id
938                      FROM {backup_ids_temp}
939                     WHERE backupid = ?
940                       AND itemname = ?",
941                    array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
942             }
943         }
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.
948         return $inforef;
949     }
952 /**
953  * This step will get all the annotations already processed to inforef.xml file and
954  * transform them into 'final' annotations.
955  */
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) {
964             // Delegate to dbops
965             backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
966         }
967     }
970 /**
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
974  * to backup storage
975  */
976 class backup_final_files_structure_step extends backup_structure_step {
978     protected function define_structure() {
980         // Define elements
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'));
990         // Build the tree
992         $files->add_child($file);
994         // Define sources
996         $file->set_source_sql("SELECT f.*
997                                  FROM {files} 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));
1002         return $files;
1003     }
1006 /**
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() {
1014         global $CFG;
1016         $info = array();
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());
1032         // Define elements
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',
1053             'directory'));
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'));
1068         // Build the tree
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);
1079         }
1080         if (!empty($cinfo['sections'])) {
1081             $contents->add_child($sections);
1082             $sections->add_child($section);
1083         }
1084         if (!empty($cinfo['course'])) {
1085             $contents->add_child($course);
1086         }
1088         $information->add_child($settings);
1089         $settings->add_child($setting);
1092         // Set the sources
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;
1108     }
1112 /**
1113  * Execution step that will generate the final zip file with all the contents
1114  */
1115 class backup_zip_contents extends backup_execution_step {
1117     protected function define_execution() {
1119         // Get basepath
1120         $basepath = $this->get_basepath();
1122         // Get the list of files in directory
1123         $filestemp = get_directory_list($basepath, '', false, true, true);
1124         $files = array();
1125         foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1126             $files[$file] = $basepath . '/' . $file;
1127         }
1129         // Add the log file if exists
1130         $logfilepath = $basepath . '.log';
1131         if (file_exists($logfilepath)) {
1132              $files['moodle_backup.log'] = $logfilepath;
1133         }
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');
1141         // Zip files
1142         $zippacker->archive_to_pathname($files, $zipfile);
1143     }
1146 /**
1147  * This step will send the generated backup file to its final destination
1148  */
1149 class backup_store_backup_file extends backup_execution_step {
1151     protected function define_execution() {
1153         // Get basepath
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));
1161     }
1165 /**
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
1168  */
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);
1180             }
1181         }
1182     }
1185 /**
1186  * This step will annotate all the groups belonging to already annotated groupings
1187  */
1188 class backup_annotate_groups_from_groupings extends backup_execution_step {
1190     protected function define_execution() {
1191         global $DB;
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);
1201                     }
1202                 }
1203             }
1204         }
1205     }
1208 /**
1209  * This step will annotate all the scales belonging to already annotated outcomes
1210  */
1211 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1213     protected function define_execution() {
1214         global $DB;
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);
1224                 }
1225             }
1226         }
1227     }
1230 /**
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)
1236  */
1237 class backup_annotate_all_user_files extends backup_execution_step {
1239     protected function define_execution() {
1240         global $DB;
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);
1257             }
1258         }
1259         $rs->close();
1260     }
1263 /**
1264  * structure step in charge of constructing the grades.xml file for all the grade items
1265  * and letters related to one activity
1266  */
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'));
1303         // Build the tree
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);
1314         // Define sources
1316         $item->set_source_sql("
1317             SELECT gi.*
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
1324         if ($userinfo) {
1325             $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1326         }
1328         $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1330         // Annotations
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)
1340         return $book;
1341     }