bc34174f8807f045f185bf251cc8ec38bf2b14aa
[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  */
28 class create_and_clean_temp_stuff extends backup_execution_step {
30     protected function define_execution() {
31         backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
32         backup_helper::clear_backup_dir($this->get_backupid());           // Empty temp dir, just in case
33         backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60));    // Delete > 4 hours temp dirs
34         backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
35     }
36 }
38 /**
39  * Create the directory where all the task (activity/block...) information will be stored
40  */
41 class create_taskbasepath_directory extends backup_execution_step {
43     protected function define_execution() {
44         global $CFG;
45         $basepath = $this->task->get_taskbasepath();
46         if (!check_dir_exists($basepath, true, true)) {
47             throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
48         }
49     }
50 }
52 /**
53  * Abtract tructure step, parent of all the activity structure steps. Used to wrap the
54  * activity structure definition within the main <activity ...> tag
55  */
56 abstract class backup_activity_structure_step extends backup_structure_step {
58     protected function prepare_activity_structure($activitystructure) {
60         // Create the wrap element
61         $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
63         // Build the tree
64         $activity->add_child($activitystructure);
66         // Set the source
67         $activityarr = array((object)array(
68             'id'         => $this->task->get_activityid(),
69             'moduleid'   => $this->task->get_moduleid(),
70             'modulename' => $this->task->get_modulename(),
71             'contextid'  => $this->task->get_contextid()));
73         $activity->set_source_array($activityarr);
75         // Return the root element (activity)
76         return $activity;
77     }
78 }
80 /**
81  * Abtract structure step, parent of all the block structure steps. Used to wrap the
82  * block structure definition within the main <block ...> tag
83  */
84 abstract class backup_block_structure_step extends backup_structure_step {
86     protected function prepare_block_structure($blockstructure) {
88         // Create the wrap element
89         $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
91         // Build the tree
92         $block->add_child($blockstructure);
94         // Set the source
95         $blockarr = array((object)array(
96             'id'         => $this->task->get_blockid(),
97             'blockname'  => $this->task->get_blockname(),
98             'contextid'  => $this->task->get_contextid()));
100         $block->set_source_array($blockarr);
102         // Return the root element (block)
103         return $block;
104     }
107 /**
108  * structure step that will generate the module.xml file for the activity,
109  * acummulating various information about the activity, annotating groupings
110  * and completion/avail conf
111  */
112 class backup_module_structure_step extends backup_structure_step {
114     protected function define_structure() {
116         // Define each element separated
118         $module = new backup_nested_element('module', array('id', 'version'), array(
119             'modulename', 'sectionid', 'sectionnumber', 'idnumber',
120             'added', 'score', 'indent', 'visible',
121             'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
122             'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
123             'availablefrom', 'availableuntil', 'showavailability'));
125         $availinfo = new backup_nested_element('availability_info');
126         $availability = new backup_nested_element('availability', array('id'), array(
127             'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
129         // Define the tree
130         $module->add_child($availinfo);
131         $availinfo->add_child($availability);
133         // Set the sources
135         $module->set_source_sql('
136             SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
137               FROM {course_modules} cm
138               JOIN {modules} m ON m.id = cm.module
139               JOIN {course_sections} s ON s.id = cm.section
140              WHERE cm.id = ?', array(backup::VAR_MODID));
142         $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
144         // Define annotations
145         $module->annotate_ids('grouping', 'groupingid');
147         // Return the root element ($module)
148         return $module;
149     }
152 /**
153  * structure step that will genereate the section.xml file for the section
154  * annotating files
155  */
156 class backup_section_structure_step extends backup_structure_step {
158     protected function define_structure() {
160         // Define each element separated
162         $section = new backup_nested_element('section', array('id'), array(
163             'number', 'name', 'summary', 'sequence', 'visible'));
165         // Define sources
167         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
169         // Aliases
170         $section->set_source_alias('section', 'number');
172         // Set annotations
173         $section->annotate_files(array('course_section'), 'id');
175         return $section;
176     }
179 /**
180  * structure step that will generate the course.xml file for the course, including
181  * course category reference, tags, metacourse, modules restriction information
182  * and some annotations (files & groupings)
183  */
184 class backup_course_structure_step extends backup_structure_step {
186     protected function define_structure() {
187         global $DB;
189         // Define each element separated
191         $course = new backup_nested_element('course', array('id', 'contextid'), array(
192             'shortname', 'fullname', 'idnumber', 'password',
193             'summary', 'summaryformat', 'format', 'showgrades',
194             'newsitems', 'guest', 'startdate', 'enrolperiod',
195             'numsections', 'marker', 'maxbytes', 'showreports',
196             'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
197             'defaultgroupingid', 'lang', 'theme', 'cost',
198             'currency', 'timecreated', 'timemodified', 'metacourse',
199             'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
200             'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
201             'enrol', 'defaultrole', 'enablecompletion'));
203         $category = new backup_nested_element('category', array('id'), array(
204             'name', 'description'));
206         $tags = new backup_nested_element('tags');
208         $tag = new backup_nested_element('tag', array('id'), array(
209             'name', 'rawname'));
211         $allowedmodules = new backup_nested_element('allowed_modules');
213         $module = new backup_nested_element('module', array('modulename'));
215         // Build the tree
217         $course->add_child($category);
219         $course->add_child($tags);
220         $tags->add_child($tag);
222         $course->add_child($allowedmodules);
223         $allowedmodules->add_child($module);
225         // Set the sources
227         $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
228         $courserec->contextid = $this->task->get_contextid();
230         $course->set_source_array(array($courserec));
232         $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
234         $category->set_source_array(array($categoryrec));
236         $tag->set_source_sql('SELECT t.id, t.name, t.rawname
237                                 FROM {tag} t
238                                 JOIN {tag_instance} ti ON ti.tagid = t.id
239                                WHERE ti.itemtype = ?
240                                  AND ti.itemid = ?', array(
241                                      $this->is_sqlparam('course'),
242                                      backup::VAR_PARENTID));
244         $module->set_source_sql('SELECT m.name AS modulename
245                                    FROM {modules} m
246                                    JOIN {course_allowed_modules} cam ON m.id = cam.module
247                                   WHERE course = ?', array(backup::VAR_COURSEID));
249         // Some annotations
251         $course->annotate_ids('role', 'defaultrole');
252         $course->annotate_ids('grouping', 'defaultgroupingid');
254         $course->annotate_files(array('course_summary', 'course_content'), null);
256         // Return root element ($course)
258         return $course;
259     }
262 /**
263  * structure step that will generate the roles.xml file for the given context, observing
264  * the role_assignments setting to know if that part needs to be included
265  */
266 class backup_roles_structure_step extends backup_structure_step {
268     protected function define_structure() {
270         // To know if we are including role assignments
271         $roleassignments = $this->get_setting_value('role_assignments');
273         // Define each element separated
275         $roles = new backup_nested_element('roles');
277         $overrides = new backup_nested_element('role_overrides');
279         $override = new backup_nested_element('override', array('id'), array(
280             'roleid', 'capability', 'permission', 'timemodified',
281             'modifierid'));
283         $assignments = new backup_nested_element('role_assignments');
285         $assignment = new backup_nested_element('assignment', array('id'), array(
286             'roleid', 'userid', 'hidden', 'timestart',
287             'timeend', 'timemodified', 'modifierid', 'enrol',
288             'sortorder'));
290         // Build the tree
291         $roles->add_child($overrides);
292         $roles->add_child($assignments);
294         $overrides->add_child($override);
295         $assignments->add_child($assignment);
297         // Define sources
299         $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
301         // Assignments only added if specified
302         if ($roleassignments) {
303             $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
304         }
306         // Define id annotations
307         $override->annotate_ids('role', 'roleid');
309         $assignment->annotate_ids('role', 'roleid');
311         $assignment->annotate_ids('user', 'userid');
313         return $roles;
314     }
317 /**
318  * structure step that will generate the roles.xml containing the
319  * list of roles used along the whole backup process. Just raw
320  * list of used roles from role table
321  */
322 class backup_final_roles_structure_step extends backup_structure_step {
324     protected function define_structure() {
326         // Define elements
328         $rolesdef = new backup_nested_element('roles_definition');
330         $role = new backup_nested_element('role', array('id'), array(
331             'name', 'shortname', 'nameincourse', 'description',
332             'sortorder', 'archetype'));
334         // Build the tree
336         $rolesdef->add_child($role);
338         // Define sources
340         $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
341                                  FROM {role} r
342                                  JOIN {backup_ids_temp} bi ON r.id = bi.itemid
343                             LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
344                                 WHERE bi.backupid = ?
345                                   AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
347         // Return main element (rolesdef)
348         return $rolesdef;
349     }
352 /**
353  * structure step that will generate the scales.xml containing the
354  * list of scales used along the whole backup process.
355  */
356 class backup_final_scales_structure_step extends backup_structure_step {
358     protected function define_structure() {
360         // Define elements
362         $scalesdef = new backup_nested_element('scales_definition');
364         $scale = new backup_nested_element('scale', array('id'), array(
365             'courseid', 'userid', 'name', 'scale',
366             'description', 'descriptionformat', 'timemodified'));
368         // Build the tree
370         $scalesdef->add_child($scale);
372         // Define sources
374         $scale->set_source_sql("SELECT s.*
375                                   FROM {scale} s
376                                   JOIN {backup_ids_temp} bi ON s.id = bi.itemid
377                                  WHERE bi.backupid = ?
378                                    AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
380         // Return main element (scalesdef)
381         return $scalesdef;
382     }
385 /**
386  * structure step that will generate the outcomes.xml containing the
387  * list of outcomes used along the whole backup process.
388  */
389 class backup_final_outcomes_structure_step extends backup_structure_step {
391     protected function define_structure() {
393         // Define elements
395         $outcomesdef = new backup_nested_element('outcomes_definition');
397         $outcome = new backup_nested_element('outcome', array('id'), array(
398             'courseid', 'userid', 'shortname', 'fullname',
399             'scaleid', 'description', 'descriptionformat', 'timecreated',
400             'timemodified','usermodified'));
402         // Build the tree
404         $outcomesdef->add_child($outcome);
406         // Define sources
408         $outcome->set_source_sql("SELECT o.*
409                                     FROM {grade_outcomes} o
410                                     JOIN {backup_ids_temp} bi ON o.id = bi.itemid
411                                    WHERE bi.backupid = ?
412                                      AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
414         // Return main element (outcomesdef)
415         return $outcomesdef;
416     }
419 /**
420  * structure step in charge of constructing the filters.xml file for all the filters found
421  * in activity
422  */
423 class backup_filters_structure_step extends backup_structure_step {
425     protected function define_structure() {
427         // Define each element separated
429         $filters = new backup_nested_element('filters');
431         $actives = new backup_nested_element('filter_actives');
433         $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
435         $configs = new backup_nested_element('filter_configs');
437         $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
439         // Build the tree
441         $filters->add_child($actives);
442         $filters->add_child($configs);
444         $actives->add_child($active);
445         $configs->add_child($config);
447         // Define sources
449         list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
451         $active->set_source_array($activearr);
452         $config->set_source_array($configarr);
454         // Return the root element (filters)
455         return $filters;
456     }
459 /**
460  * structure step in charge of constructing the comments.xml file for all the comments found
461  * in a given context
462  */
463 class backup_comments_structure_step extends backup_structure_step {
465     protected function define_structure() {
467         // Define each element separated
469         $comments = new backup_nested_element('comments');
471         $comment = new backup_nested_element('comment', array('id'), array(
472             'commentarea', 'itemid', 'content', 'format',
473             'userid', 'timecreated'));
475         // Build the tree
477         $comments->add_child($comment);
479         // Define sources
481         $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
483         // Define id annotations
485         $comment->annotate_ids('user', 'userid');
487         // Return the root element (comments)
488         return $comments;
489     }
492 /**
493  * structure step in charge if constructing the completion.xml file for all the users completion
494  * information in a given activity
495  */
496 class backup_userscompletion_structure_step extends backup_structure_step {
498     protected function define_structure() {
500         // Define each element separated
502         $completions = new backup_nested_element('completions');
504         $completion = new backup_nested_element('completion', array('id'), array(
505             'userid', 'completionstate', 'viewed', 'timemodified'));
507         // Build the tree
509         $completions->add_child($completion);
511         // Define sources
513         $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
515         // Define id annotations
517         $completion->annotate_ids('user', 'userid');
519         // Return the root element (completions)
520         return $completions;
521     }
524 /**
525  * structure step in charge of constructing the main groups.xml file for all the groups and
526  * groupings information already annotated
527  */
528 class backup_groups_structure_step extends backup_structure_step {
530     protected function define_structure() {
532         // To know if we are including users
533         $users = $this->get_setting_value('users');
535         // Define each element separated
537         $groups = new backup_nested_element('groups');
539         $group = new backup_nested_element('group', array('id'), array(
540             'name', 'description', 'descriptionformat', 'enrolmentkey',
541             'picture', 'hidepicture', 'timecreated', 'timemodified'));
543         $members = new backup_nested_element('group_members');
545         $member = new backup_nested_element('group_member', array('id'), array(
546             'userid', 'timeadded'));
548         $groupings = new backup_nested_element('groupings');
550         $grouping = new backup_nested_element('grouping', 'id', array(
551             'name', 'description', 'descriptionformat', 'configdata',
552             'timecreated', 'timemodified'));
554         $groupinggroups = new backup_nested_element('grouping_groups');
556         $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
557             'groupid', 'timeadded'));
559         // Build the tree
561         $groups->add_child($group);
562         $groups->add_child($groupings);
564         $group->add_child($members);
565         $members->add_child($member);
567         $groupings->add_child($grouping);
568         $grouping->add_child($groupinggroups);
569         $groupinggroups->add_child($groupinggroup);
571         // Define sources
573         $group->set_source_sql("
574             SELECT g.*
575               FROM {groups} g
576               JOIN {backup_ids_temp} bi ON g.id = bi.itemid
577              WHERE bi.backupid = ?
578                AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
580         // This only happens if we are including users
581         if ($users) {
582             $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
583         }
585         $grouping->set_source_sql("
586             SELECT g.*
587               FROM {groupings} g
588               JOIN {backup_ids_temp} bi ON g.id = bi.itemid
589              WHERE bi.backupid = ?
590                AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
592         $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
594         // Define id annotations (as final)
596         $member->annotate_ids('userfinal', 'userid');
598         // Define file annotations
600         // TODO: Change "course_group_image" file area to the one finally used for group images
601         $group->annotate_files(array('course_group_description', 'course_group_image'), 'id');
603         // Return the root element (groups)
604         return $groups;
605     }
608 /**
609  * structure step in charge of constructing the main users.xml file for all the users already
610  * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
611  * overrides.
612  */
613 class backup_users_structure_step extends backup_structure_step {
615     protected function define_structure() {
616         global $CFG;
618         // To know if we are anonymizing users
619         $anonymize = $this->get_setting_value('anonymize');
620         // To know if we are including role assignments
621         $roleassignments = $this->get_setting_value('role_assignments');
623         // Define each element separated
625         $users = new backup_nested_element('users');
627         // Create the array of user fields by hand, as far as we have various bits to control
628         // anonymize option, password backup, mnethostid...
630         // First, the fields not needing anonymization nor special handling
631         $normalfields = array(
632             'confirmed', 'policyagreed', 'deleted',
633             'lang', 'theme', 'timezone', 'firstaccess',
634             'lastaccess', 'lastlogin', 'currentlogin', 'secret',
635             'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
636             'ajax', 'autosubscribe', 'trackforums', 'timecreated',
637             'timemodified', 'trustbitmask', 'screenreader');
639         // Then, the fields potentially needing anonymization
640         $anonfields = array(
641             'username', 'idnumber', 'firstname', 'lastname',
642             'email', 'emailstop', 'lastip', 'picture',
643             'url', 'description', 'description_format', 'imagealt', 'auth');
645         // Add anonymized fields to $userfields with custom final element
646         foreach ($anonfields as $field) {
647             if ($anonymize) {
648                 $userfields[] = new anonymizer_final_element($field);
649             } else {
650                 $userfields[] = $field; // No anonymization, normally added
651             }
652         }
654         // mnethosturl requires special handling (custom final element)
655         $userfields[] = new mnethosturl_final_element('mnethosturl');
657         // password added conditionally
658         if (!empty($CFG->includeuserpasswordsinbackup)) {
659             $userfields[] = 'password';
660         }
662         // Merge all the fields
663         $userfields = array_merge($userfields, $normalfields);
665         $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
667         $customfields = new backup_nested_element('custom_fields');
669         $customfield = new backup_nested_element('custom_field', array('id'), array(
670             'field_name', 'field_type', 'field_data'));
672         $tags = new backup_nested_element('tags');
674         $tag = new backup_nested_element('tag', array('id'), array(
675             'name', 'rawname'));
677         $preferences = new backup_nested_element('preferences');
679         $preference = new backup_nested_element('preference', array('id'), array(
680             'name', 'value'));
682         $roles = new backup_nested_element('roles');
684         $overrides = new backup_nested_element('role_overrides');
686         $override = new backup_nested_element('override', array('id'), array(
687             'roleid', 'capability', 'permission', 'timemodified',
688             'modifierid'));
690         $assignments = new backup_nested_element('role_assignments');
692         $assignment = new backup_nested_element('assignment', array('id'), array(
693             'roleid', 'userid', 'hidden', 'timestart',
694             'timeend', 'timemodified', 'modifierid', 'enrol',
695             'sortorder'));
697         // Build the tree
699         $users->add_child($user);
701         $user->add_child($customfields);
702         $customfields->add_child($customfield);
704         $user->add_child($tags);
705         $tags->add_child($tag);
707         $user->add_child($preferences);
708         $preferences->add_child($preference);
710         $user->add_child($roles);
712         $roles->add_child($overrides);
713         $roles->add_child($assignments);
715         $overrides->add_child($override);
716         $assignments->add_child($assignment);
718         // Define sources
720         $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
721                                  FROM {user} u
722                                  JOIN {backup_ids_temp} bi ON bi.itemid = u.id
723                                  JOIN {context} c ON c.instanceid = u.id
724                             LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
725                                 WHERE bi.backupid = ?
726                                   AND bi.itemname = ?
727                                   AND c.contextlevel = ?', array(
728                                       $this->is_sqlparam($this->get_backupid()),
729                                       $this->is_sqlparam('userfinal'),
730                                       $this->is_sqlparam(CONTEXT_USER)));
732         // All the rest on information is only added if we arent
733         // in an anonymized backup
734         if (!$anonymize) {
735             $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
736                                             FROM {user_info_field} f
737                                             JOIN {user_info_data} d ON d.fieldid = f.id
738                                            WHERE d.userid = ?', array(backup::VAR_PARENTID));
740             $customfield->set_source_alias('shortname', 'field_name');
741             $customfield->set_source_alias('datatype',  'field_type');
742             $customfield->set_source_alias('data',      'field_data');
744             $tag->set_source_sql('SELECT t.id, t.name, t.rawname
745                                     FROM {tag} t
746                                     JOIN {tag_instance} ti ON ti.tagid = t.id
747                                    WHERE ti.itemtype = ?
748                                      AND ti.itemid = ?', array(
749                                          $this->is_sqlparam('user'),
750                                          backup::VAR_PARENTID));
752             $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
754             $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
756             // Assignments only added if specified
757             if ($roleassignments) {
758                 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
759             }
761             // Define id annotations (as final)
762             $override->annotate_ids('rolefinal', 'roleid');
763         }
765         // Return root element (users)
766         return $users;
767     }
770 /**
771  * structure step in charge of constructing the block.xml file for one
772  * given block (intance and positions). If the block has custom DB structure
773  * that will go to a separate file (different step defined in block class)
774  */
775 class backup_block_instance_structure_step extends backup_structure_step {
777     protected function define_structure() {
778         global $DB;
780         // Define each element separated
782         $block = new backup_nested_element('block', array('id', 'version'), array(
783             'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
784             'defaultregion', 'defaultweight', 'configdata'));
786         $positions = new backup_nested_element('block_positions', null, array(
787             'contextid', 'pagetype', 'subpage', 'visible',
788             'region', 'weight'));
790         // Build the tree
792         $block->add_child($positions);
794         // Transform configdata information if needed (process links and friends)
795         $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
796         if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
797             $configdata = (array)unserialize(base64_decode($blockrec->configdata));
798             foreach ($configdata as $attribute => $value) {
799                 if (in_array($attribute, $attrstotransform)) {
800                     $configdata[$attribute] = $this->contenttransformer->process($value);
801                 }
802             }
803             $blockrec->configdata = base64_encode(serialize((object)$configdata));
804         }
805         // Get the version of the block
806         $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
808         // Define sources
810         $block->set_source_array(array($blockrec));
812         $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
814         // Return the root element (block)
815         return $block;
816     }
819 /**
820  * structure step in charge of constructing the logs.xml file for all the log records found
821  * in activity
822  */
823 class backup_activity_logs_structure_step extends backup_structure_step {
825     protected function define_structure() {
827         // Define each element separated
829         $logs = new backup_nested_element('logs');
831         $log = new backup_nested_element('log', array('id'), array(
832             'time', 'userid', 'ip', 'module',
833             'action', 'url', 'info'));
835         // Build the tree
837         $logs->add_child($log);
839         // Define sources
841         $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
843         // Annotations
844         // NOTE: We don't annotate users from logs as far as they MUST be
845         //       always annotated by the activity.
847         // Return the root element (logs)
849         return $logs;
850     }
853 /**
854  * structure in charge of constructing the inforef.xml file for all the items we want
855  * to have referenced there (users, roles, files...)
856  */
857 class backup_inforef_structure_step extends backup_structure_step {
859     protected function define_structure() {
861         // Items we want to include in the inforef file. NOTE: Important to keep this
862         // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
863         $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
865         // Build the tree
867         $inforef = new backup_nested_element('inforef');
869         // For each item, conditionally, if there are already records, build element
870         foreach ($items as $itemname) {
871             if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
872                 $elementroot = new backup_nested_element($itemname . 'ref');
873                 $element = new backup_nested_element($itemname, array('id'));
874                 $inforef->add_child($elementroot);
875                 $elementroot->add_child($element);
876                 $element->set_source_sql("
877                     SELECT itemid AS id
878                      FROM {backup_ids_temp}
879                     WHERE backupid = ?
880                       AND itemname = ?",
881                    array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
882             }
883         }
885         // We don't annotate anything there, but rely in the next step
886         // (move_inforef_annotations_to_final) that will change all the
887         // already saved 'inforref' entries to their 'final' annotations.
888         return $inforef;
889     }
892 /**
893  * This step will get all the annotations already processed to inforef.xml file and
894  * transform them into 'final' annotations.
895  */
896 class move_inforef_annotations_to_final extends backup_execution_step {
898     protected function define_execution() {
900         // Items we want to include in the inforef file. NOTE: Important to keep this
901         // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
902         $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
903         foreach ($items as $itemname) {
904             // Delegate to dbops
905             backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
906         }
907     }
910 /**
911  * structure in charge of constructing the files.xml file with all the
912  * annotated (final) files along the process. At, the same time, and
913  * using one specialised nested_element, will copy them form moodle storage
914  * to backup storage
915  */
916 class backup_final_files_structure_step extends backup_structure_step {
918     protected function define_structure() {
920         // Define elements
922         $files = new backup_nested_element('files');
924         $file = new file_nested_element('file', array('id'), array(
925             'contenthash', 'contextid', 'filearea', 'itemid',
926             'filepath', 'filename', 'userid', 'filesize',
927             'mimetype', 'status', 'timecreated', 'timemodified',
928             'source', 'author', 'license'));
930         // Build the tree
932         $files->add_child($file);
934         // Define sources
936         $file->set_source_sql("SELECT f.*
937                                  FROM {files} f
938                                  JOIN {backup_ids_temp} bi ON f.id = bi.itemid
939                                 WHERE bi.backupid = ?
940                                   AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
942         return $files;
943     }
946 /**
947  * Structure step in charge of creating the main moodle_backup.xml file
948  * where all the information related to the backup, settings, license and
949  * other information needed on restore is added*/
950 class backup_main_structure_step extends backup_structure_step {
952     protected function define_structure() {
954         global $CFG;
956         $info = array();
958         $info['name'] = $this->get_setting_value('filename');
959         $info['moodle_version'] = $CFG->version;
960         $info['moodle_release'] = $CFG->release;
961         $info['backup_version'] = $CFG->backup_version;
962         $info['backup_release'] = $CFG->backup_release;
963         $info['backup_date']    = time();
964         $info['backup_uniqueid']= $this->get_backupid();
965         $info['original_wwwroot']=$CFG->wwwroot;
966         $info['original_site_identifier'] = get_site_identifier();
967         $info['original_course_id'] = $this->get_courseid();
969         // Get more information from controller
970         list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
972         // Define elements
974         $moodle_backup = new backup_nested_element('moodle_backup');
976         $information = new backup_nested_element('information', null, array(
977             'name', 'moodle_version', 'moodle_release', 'backup_version',
978             'backup_release', 'backup_date', 'original_wwwroot',
979             'original_site_identifier', 'original_course_id'));
981         $details = new backup_nested_element('details');
983         $detail = new backup_nested_element('detail', array('backup_id'), array(
984             'type', 'format', 'interactive', 'mode',
985             'execution', 'executiontime'));
987         $contents = new backup_nested_element('contents');
989         $activities = new backup_nested_element('activities');
991         $activity = new backup_nested_element('activity', null, array(
992             'moduleid', 'sectionid', 'modulename', 'title',
993             'directory'));
995         $sections = new backup_nested_element('sections');
997         $section = new backup_nested_element('section', null, array(
998             'sectionid', 'title', 'directory'));
1000         $course = new backup_nested_element('course', null, array(
1001             'courseid', 'title', 'directory'));
1003         $settings = new backup_nested_element('settings');
1005         $setting = new backup_nested_element('setting', null, array(
1006             'level', 'activity', 'name', 'value'));
1008         // Build the tree
1010         $moodle_backup->add_child($information);
1012         $information->add_child($details);
1013         $details->add_child($detail);
1015         $information->add_child($contents);
1016         if (!empty($cinfo['activities'])) {
1017             $contents->add_child($activities);
1018             $activities->add_child($activity);
1019         }
1020         if (!empty($cinfo['sections'])) {
1021             $contents->add_child($sections);
1022             $sections->add_child($section);
1023         }
1024         if (!empty($cinfo['course'])) {
1025             $contents->add_child($course);
1026         }
1028         $information->add_child($settings);
1029         $settings->add_child($setting);
1032         // Set the sources
1034         $information->set_source_array(array((object)$info));
1036         $detail->set_source_array($dinfo);
1038         $activity->set_source_array($cinfo['activities']);
1040         $section->set_source_array($cinfo['sections']);
1042         $course->set_source_array($cinfo['course']);
1044         $setting->set_source_array($sinfo);
1046         // Prepare some information to be sent to main moodle_backup.xml file
1047         return $moodle_backup;
1048     }
1052 /**
1053  * Execution step that will generate the final zip file with all the contents
1054  */
1055 class backup_zip_contents extends backup_execution_step {
1057     protected function define_execution() {
1059         // Get basepath
1060         $basepath = $this->get_basepath();
1062         // Get the list of files in directory
1063         $filestemp = get_directory_list($basepath, '', false, true, true);
1064         $files = array();
1065         foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1066             $files[$file] = $basepath . '/' . $file;
1067         }
1069         // Add the log file if exists
1070         $logfilepath = $basepath . '.log';
1071         if (file_exists($logfilepath)) {
1072              $files['moodle_backup.log'] = $logfilepath;
1073         }
1075         // Calculate the zip fullpath
1076         $zipfile = $basepath . '/' . $this->get_setting_value('filename');
1078         // Get the zip packer
1079         $zippacker = get_file_packer('application/zip');
1081         // Zip files
1082         $zippacker->archive_to_pathname($files, $zipfile);
1083     }
1086 /**
1087  * This step will send the generated backup file to its final destination
1088  */
1089 class backup_store_backup_file extends backup_execution_step {
1091     protected function define_execution() {
1093         // Get basepath
1094         $basepath = $this->get_basepath();
1096         // Calculate the zip fullpath
1097         $zipfile = $basepath . '/' . $this->get_setting_value('filename');
1099         // Perform storage and return it (TODO: shouldn't be array but proper result object)
1100         return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
1101     }
1105 /**
1106  * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1107  * and put them to the backup_ids tables, to be used later as base to backup them
1108  */
1109 class backup_activity_grade_items_to_ids extends backup_execution_step {
1111     protected function define_execution() {
1113         // Fetch all activity grade items
1114         if ($items = grade_item::fetch_all(array(
1115                          'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1116                          'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1117             // Annotate them in backup_ids
1118             foreach ($items as $item) {
1119                 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1120             }
1121         }
1122     }
1125 /**
1126  * This step will annotate all the groups belonging to already annotated groupings
1127  */
1128 class backup_annotate_groups_from_groupings extends backup_execution_step {
1130     protected function define_execution() {
1131         global $DB;
1133         // Fetch all the annotated groupings
1134         if ($groupings = $DB->get_records('backup_ids_temp', array(
1135                 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1136             foreach ($groupings as $grouping) {
1137                 if ($groups = $DB->get_records('groupings_groups', array(
1138                         'groupingid' => $grouping->itemid))) {
1139                     foreach ($groups as $group) {
1140                         backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1141                     }
1142                 }
1143             }
1144         }
1145     }
1148 /**
1149  * This step will annotate all the scales belonging to already annotated outcomes
1150  */
1151 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1153     protected function define_execution() {
1154         global $DB;
1156         // Fetch all the annotated outcomes
1157         if ($outcomes = $DB->get_records('backup_ids_temp', array(
1158                 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1159             foreach ($outcomes as $outcome) {
1160                 if ($scale = $DB->get_record('grade_outcomes', array(
1161                         'id' => $outcome->itemid))) {
1162                     // Annotate as scalefinal because it's > 0
1163                     backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1164                 }
1165             }
1166         }
1167     }
1170 /**
1171  * This step will generate all the user annotations for the already
1172  * annottated (final) users. Need to do this here because each user
1173  * has its own context and structure tasks only are able to handle
1174  * one context. Also, this step will guarantee that every user has
1175  * its context created (req for other steps)
1176  */
1177 class backup_annotate_all_user_files extends backup_execution_step {
1179     protected function define_execution() {
1180         global $DB;
1182         // List of fileareas we are going to annotate
1183         // TODO: Change "user_image" file area to the one finally used for user images
1184         $fileareas = array(
1185             'user_private', 'user_profile', 'user_image');
1187         // Fetch all annotated (final) users
1188         $rs = $DB->get_recordset('backup_ids_temp', array(
1189             'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1190         foreach ($rs as $record) {
1191             $userid = $record->itemid;
1192             $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1193             // Proceed with every user filearea
1194             foreach ($fileareas as $filearea) {
1195                 // We don't need to specify itemid ($userid - 4th param) as far as by
1196                 // context we can get all the associated files. See MDL-22092
1197                 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, $filearea, null);
1198             }
1199         }
1200         $rs->close();
1201     }
1204 /**
1205  * structure step in charge of constructing the grades.xml file for all the grade items
1206  * and letters related to one activity
1207  */
1208 class backup_activity_grades_structure_step extends backup_structure_step {
1210     protected function define_structure() {
1212         // To know if we are including userinfo
1213         $userinfo = $this->get_setting_value('userinfo');
1215         // Define each element separated
1217         $book = new backup_nested_element('activity_gradebook');
1219         $items = new backup_nested_element('grade_items');
1221         $item = new backup_nested_element('grade_item', array('id'), array(
1222             'categoryid', 'itemname', 'itemtype', 'itemmodule',
1223             'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1224             'calculation', 'gradetype', 'grademax', 'grademin',
1225             'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1226             'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1227             'decimals', 'hidden', 'locked', 'locktime',
1228             'needsupdate', 'timecreated', 'timemodified'));
1230         $grades = new backup_nested_element('grade_grades');
1232         $grade = new backup_nested_element('grade_grade', array('id'), array(
1233             'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1234             'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1235             'locked', 'locktime', 'exported', 'overridden',
1236             'excluded', 'feedback', 'feedbackformat', 'information',
1237             'informationformat', 'timecreated', 'timemodified'));
1239         $letters = new backup_nested_element('grade_letters');
1241         $letter = new backup_nested_element('grade_letter', 'id', array(
1242             'lowerboundary', 'letter'));
1244         // Build the tree
1246         $book->add_child($items);
1247         $items->add_child($item);
1249         $item->add_child($grades);
1250         $grades->add_child($grade);
1252         $book->add_child($letters);
1253         $letters->add_child($letter);
1255         // Define sources
1257         $item->set_source_sql("
1258             SELECT gi.*
1259               FROM {grade_items} gi
1260               JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1261              WHERE bi.backupid = ?
1262                AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1264         // This only happens if we are including user info
1265         if ($userinfo) {
1266             $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1267         }
1269         $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1271         // Annotations
1273         $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1274         $item->annotate_ids('outcome', 'outcomeid');
1276         $grade->annotate_ids('user', 'userid');
1277         $grade->annotate_ids('user', 'usermodified');
1279         // Return the root element (book)
1281         return $book;
1282     }