MDL-21432 backup - clean temps after execution
[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
79  */
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);
87         // Build the tree
88         $activity->add_child($activitystructure);
90         // Set the source
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)
100         return $activity;
101     }
104 /**
105  * Abtract structure step, parent of all the block structure steps. Used to wrap the
106  * block structure definition within the main <block ...> tag
107  */
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);
115         // Build the tree
116         $block->add_child($blockstructure);
118         // Set the source
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)
127         return $block;
128     }
131 /**
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
135  */
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'));
153         // Define the tree
154         $module->add_child($availinfo);
155         $availinfo->add_child($availability);
157         // Set the sources
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)
172         return $module;
173     }
176 /**
177  * structure step that will genereate the section.xml file for the section
178  * annotating files
179  */
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'));
189         // Define sources
191         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
193         // Aliases
194         $section->set_source_alias('section', 'number');
196         // Set annotations
197         $section->annotate_files(array('course_section'), 'id');
199         return $section;
200     }
203 /**
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)
207  */
208 class backup_course_structure_step extends backup_structure_step {
210     protected function define_structure() {
211         global $DB;
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(
233             'name', 'rawname'));
235         $allowedmodules = new backup_nested_element('allowed_modules');
237         $module = new backup_nested_element('module', array('modulename'));
239         // Build the tree
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);
249         // Set the sources
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
261                                 FROM {tag} t
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
269                                    FROM {modules} m
270                                    JOIN {course_allowed_modules} cam ON m.id = cam.module
271                                   WHERE course = ?', array(backup::VAR_COURSEID));
273         // Some annotations
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)
282         return $course;
283     }
286 /**
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
289  */
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',
305             'modifierid'));
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',
312             'sortorder'));
314         // Build the tree
315         $roles->add_child($overrides);
316         $roles->add_child($assignments);
318         $overrides->add_child($override);
319         $assignments->add_child($assignment);
321         // Define sources
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));
328         }
330         // Define id annotations
331         $override->annotate_ids('role', 'roleid');
333         $assignment->annotate_ids('role', 'roleid');
335         $assignment->annotate_ids('user', 'userid');
337         return $roles;
338     }
341 /**
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
345  */
346 class backup_final_roles_structure_step extends backup_structure_step {
348     protected function define_structure() {
350         // Define elements
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'));
358         // Build the tree
360         $rolesdef->add_child($role);
362         // Define sources
364         $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
365                                  FROM {role} r
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)
372         return $rolesdef;
373     }
376 /**
377  * structure step that will generate the scales.xml containing the
378  * list of scales used along the whole backup process.
379  */
380 class backup_final_scales_structure_step extends backup_structure_step {
382     protected function define_structure() {
384         // Define elements
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'));
392         // Build the tree
394         $scalesdef->add_child($scale);
396         // Define sources
398         $scale->set_source_sql("SELECT s.*
399                                   FROM {scale} 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)
405         return $scalesdef;
406     }
409 /**
410  * structure step that will generate the outcomes.xml containing the
411  * list of outcomes used along the whole backup process.
412  */
413 class backup_final_outcomes_structure_step extends backup_structure_step {
415     protected function define_structure() {
417         // Define elements
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'));
426         // Build the tree
428         $outcomesdef->add_child($outcome);
430         // Define sources
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)
439         return $outcomesdef;
440     }
443 /**
444  * structure step in charge of constructing the filters.xml file for all the filters found
445  * in activity
446  */
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'));
463         // Build the tree
465         $filters->add_child($actives);
466         $filters->add_child($configs);
468         $actives->add_child($active);
469         $configs->add_child($config);
471         // Define sources
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)
479         return $filters;
480     }
483 /**
484  * structure step in charge of constructing the comments.xml file for all the comments found
485  * in a given context
486  */
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'));
499         // Build the tree
501         $comments->add_child($comment);
503         // Define sources
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)
512         return $comments;
513     }
516 /**
517  * structure step in charge if constructing the completion.xml file for all the users completion
518  * information in a given activity
519  */
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'));
531         // Build the tree
533         $completions->add_child($completion);
535         // Define sources
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)
544         return $completions;
545     }
548 /**
549  * structure step in charge of constructing the main groups.xml file for all the groups and
550  * groupings information already annotated
551  */
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'));
583         // Build the tree
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);
595         // Define sources
597         $group->set_source_sql("
598             SELECT g.*
599               FROM {groups} g
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
605         if ($users) {
606             $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
607         }
609         $grouping->set_source_sql("
610             SELECT g.*
611               FROM {groupings} g
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)
628         return $groups;
629     }
632 /**
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
635  * overrides.
636  */
637 class backup_users_structure_step extends backup_structure_step {
639     protected function define_structure() {
640         global $CFG;
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
664         $anonfields = array(
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) {
671             if ($anonymize) {
672                 $userfields[] = new anonymizer_final_element($field);
673             } else {
674                 $userfields[] = $field; // No anonymization, normally added
675             }
676         }
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';
684         }
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(
699             'name', 'rawname'));
701         $preferences = new backup_nested_element('preferences');
703         $preference = new backup_nested_element('preference', array('id'), array(
704             'name', 'value'));
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',
712             'modifierid'));
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',
719             'sortorder'));
721         // Build the tree
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);
742         // Define sources
744         $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
745                                  FROM {user} u
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 = ?
750                                   AND bi.itemname = ?
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
758         if (!$anonymize) {
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
769                                     FROM {tag} t
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'));
783             }
785             // Define id annotations (as final)
786             $override->annotate_ids('rolefinal', 'roleid');
787         }
789         // Return root element (users)
790         return $users;
791     }
794 /**
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)
798  */
799 class backup_block_instance_structure_step extends backup_structure_step {
801     protected function define_structure() {
802         global $DB;
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'));
814         // Build the tree
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);
825                 }
826             }
827             $blockrec->configdata = base64_encode(serialize((object)$configdata));
828         }
829         // Get the version of the block
830         $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
832         // Define sources
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)
839         return $block;
840     }
843 /**
844  * structure step in charge of constructing the logs.xml file for all the log records found
845  * in activity
846  */
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'));
859         // Build the tree
861         $logs->add_child($log);
863         // Define sources
865         $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
867         // Annotations
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)
873         return $logs;
874     }
877 /**
878  * structure in charge of constructing the inforef.xml file for all the items we want
879  * to have referenced there (users, roles, files...)
880  */
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');
889         // Build the tree
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("
901                     SELECT itemid AS id
902                      FROM {backup_ids_temp}
903                     WHERE backupid = ?
904                       AND itemname = ?",
905                    array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
906             }
907         }
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.
912         return $inforef;
913     }
916 /**
917  * This step will get all the annotations already processed to inforef.xml file and
918  * transform them into 'final' annotations.
919  */
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) {
928             // Delegate to dbops
929             backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
930         }
931     }
934 /**
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
938  * to backup storage
939  */
940 class backup_final_files_structure_step extends backup_structure_step {
942     protected function define_structure() {
944         // Define elements
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'));
954         // Build the tree
956         $files->add_child($file);
958         // Define sources
960         $file->set_source_sql("SELECT f.*
961                                  FROM {files} 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));
966         return $files;
967     }
970 /**
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() {
978         global $CFG;
980         $info = array();
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());
996         // Define elements
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',
1017             'directory'));
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'));
1032         // Build the tree
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);
1043         }
1044         if (!empty($cinfo['sections'])) {
1045             $contents->add_child($sections);
1046             $sections->add_child($section);
1047         }
1048         if (!empty($cinfo['course'])) {
1049             $contents->add_child($course);
1050         }
1052         $information->add_child($settings);
1053         $settings->add_child($setting);
1056         // Set the sources
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;
1072     }
1076 /**
1077  * Execution step that will generate the final zip file with all the contents
1078  */
1079 class backup_zip_contents extends backup_execution_step {
1081     protected function define_execution() {
1083         // Get basepath
1084         $basepath = $this->get_basepath();
1086         // Get the list of files in directory
1087         $filestemp = get_directory_list($basepath, '', false, true, true);
1088         $files = array();
1089         foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1090             $files[$file] = $basepath . '/' . $file;
1091         }
1093         // Add the log file if exists
1094         $logfilepath = $basepath . '.log';
1095         if (file_exists($logfilepath)) {
1096              $files['moodle_backup.log'] = $logfilepath;
1097         }
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');
1105         // Zip files
1106         $zippacker->archive_to_pathname($files, $zipfile);
1107     }
1110 /**
1111  * This step will send the generated backup file to its final destination
1112  */
1113 class backup_store_backup_file extends backup_execution_step {
1115     protected function define_execution() {
1117         // Get basepath
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));
1125     }
1129 /**
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
1132  */
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);
1144             }
1145         }
1146     }
1149 /**
1150  * This step will annotate all the groups belonging to already annotated groupings
1151  */
1152 class backup_annotate_groups_from_groupings extends backup_execution_step {
1154     protected function define_execution() {
1155         global $DB;
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);
1165                     }
1166                 }
1167             }
1168         }
1169     }
1172 /**
1173  * This step will annotate all the scales belonging to already annotated outcomes
1174  */
1175 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1177     protected function define_execution() {
1178         global $DB;
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);
1188                 }
1189             }
1190         }
1191     }
1194 /**
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)
1200  */
1201 class backup_annotate_all_user_files extends backup_execution_step {
1203     protected function define_execution() {
1204         global $DB;
1206         // List of fileareas we are going to annotate
1207         // TODO: Change "user_image" file area to the one finally used for user images
1208         $fileareas = array(
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);
1222             }
1223         }
1224         $rs->close();
1225     }
1228 /**
1229  * structure step in charge of constructing the grades.xml file for all the grade items
1230  * and letters related to one activity
1231  */
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'));
1268         // Build the tree
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);
1279         // Define sources
1281         $item->set_source_sql("
1282             SELECT gi.*
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
1289         if ($userinfo) {
1290             $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1291         }
1293         $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1295         // Annotations
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)
1305         return $book;
1306     }