0092b163029a3296869e7af451b3e6abfa440a29
[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', 'summary', 'sequence', 'visible'));
165         // Define sources
167         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
169         // Set annotations
170         $section->annotate_files(array('course_section'), 'id');
172         return $section;
173     }
176 /**
177  * structure step that will generate the course.xml file for the course, including
178  * course category reference, tags, metacourse, modules restriction information
179  * and some annotations (files & groupings)
180  */
181 class backup_course_structure_step extends backup_structure_step {
183     protected function define_structure() {
184         global $DB;
186         // Define each element separated
188         $course = new backup_nested_element('course', array('id', 'contextid'), array(
189             'shortname', 'fullname', 'idnumber', 'password',
190             'summary', 'summaryformat', 'format', 'showgrades',
191             'newsitems', 'guest', 'startdate', 'enrolperiod',
192             'numsections', 'marker', 'maxbytes', 'showreports',
193             'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
194             'defaultgroupingid', 'lang', 'theme', 'cost',
195             'currency', 'timecreated', 'timemodified', 'metacourse',
196             'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
197             'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
198             'enrol', 'defaultrole', 'enablecompletion'));
200         $category = new backup_nested_element('category', array('id'), array(
201             'name', 'description'));
203         $tags = new backup_nested_element('tags');
205         $tag = new backup_nested_element('tag', array('id'), array(
206             'name', 'rawname'));
208         $allowedmodules = new backup_nested_element('allowed_modules');
210         $module = new backup_nested_element('module', array('modulename'));
212         // Build the tree
214         $course->add_child($category);
216         $course->add_child($tags);
217         $tags->add_child($tag);
219         $course->add_child($allowedmodules);
220         $allowedmodules->add_child($module);
222         // Set the sources
224         $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
225         $courserec->contextid = $this->task->get_contextid();
227         $course->set_source_array(array($courserec));
229         $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
231         $category->set_source_array(array($categoryrec));
233         $tag->set_source_sql('SELECT t.id, t.name, t.rawname
234                                 FROM {tag} t
235                                 JOIN {tag_instance} ti ON ti.tagid = t.id
236                                WHERE ti.itemtype = ?
237                                  AND ti.itemid = ?', array(
238                                      $this->is_sqlparam('course'),
239                                      backup::VAR_PARENTID));
241         $module->set_source_sql('SELECT m.name AS modulename
242                                    FROM {modules} m
243                                    JOIN {course_allowed_modules} cam ON m.id = cam.module
244                                   WHERE course = ?', array(backup::VAR_COURSEID));
246         // Some annotations
248         $course->annotate_ids('role', 'defaultrole');
249         $course->annotate_ids('grouping', 'defaultgroupingid');
251         $course->annotate_files(array('course_summary', 'course_content'), null);
253         // Return root element ($course)
255         return $course;
256     }
259 /**
260  * structure step that will generate the roles.xml file for the given context, observing
261  * the role_assignments setting to know if that part needs to be included
262  */
263 class backup_roles_structure_step extends backup_structure_step {
265     protected function define_structure() {
267         // To know if we are including role assignments
268         $roleassignments = $this->get_setting_value('role_assignments');
270         // Define each element separated
272         $roles = new backup_nested_element('roles');
274         $overrides = new backup_nested_element('role_overrides');
276         $override = new backup_nested_element('override', array('id'), array(
277             'roleid', 'capability', 'permission', 'timemodified',
278             'modifierid'));
280         $assignments = new backup_nested_element('role_assignments');
282         $assignment = new backup_nested_element('assignment', array('id'), array(
283             'roleid', 'userid', 'hidden', 'timestart',
284             'timeend', 'timemodified', 'modifierid', 'enrol',
285             'sortorder'));
287         // Build the tree
288         $roles->add_child($overrides);
289         $roles->add_child($assignments);
291         $overrides->add_child($override);
292         $assignments->add_child($assignment);
294         // Define sources
296         $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
298         // Assignments only added if specified
299         if ($roleassignments) {
300             $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
301         }
303         // Define id annotations
304         $override->annotate_ids('role', 'roleid');
306         $assignment->annotate_ids('role', 'roleid');
308         $assignment->annotate_ids('user', 'userid');
310         return $roles;
311     }
314 /**
315  * structure step that will generate the roles.xml containing the
316  * list of roles used along the whole backup process. Just raw
317  * list of used roles from role table
318  */
319 class backup_final_roles_structure_step extends backup_structure_step {
321     protected function define_structure() {
323         // Define elements
325         $rolesdef = new backup_nested_element('roles_definition');
327         $role = new backup_nested_element('role', array('id'), array(
328             'name', 'shortname', 'nameincourse', 'description',
329             'sortorder', 'archetype'));
331         // Build the tree
333         $rolesdef->add_child($role);
335         // Define sources
337         $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
338                                  FROM {role} r
339                                  JOIN {backup_ids_temp} bi ON r.id = bi.itemid
340                             LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
341                                 WHERE bi.backupid = ?
342                                   AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
344         // Return main element (rolesdef)
345         return $rolesdef;
346     }
349 /**
350  * structure step that will generate the scales.xml containing the
351  * list of scales used along the whole backup process.
352  */
353 class backup_final_scales_structure_step extends backup_structure_step {
355     protected function define_structure() {
357         // Define elements
359         $scalesdef = new backup_nested_element('scales_definition');
361         $scale = new backup_nested_element('scale', array('id'), array(
362             'courseid', 'userid', 'name', 'scale',
363             'description', 'descriptionformat', 'timemodified'));
365         // Build the tree
367         $scalesdef->add_child($scale);
369         // Define sources
371         $scale->set_source_sql("SELECT s.*
372                                   FROM {scale} s
373                                   JOIN {backup_ids_temp} bi ON s.id = bi.itemid
374                                  WHERE bi.backupid = ?
375                                    AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
377         // Return main element (scalesdef)
378         return $scalesdef;
379     }
382 /**
383  * structure step that will generate the outcomes.xml containing the
384  * list of outcomes used along the whole backup process.
385  */
386 class backup_final_outcomes_structure_step extends backup_structure_step {
388     protected function define_structure() {
390         // Define elements
392         $outcomesdef = new backup_nested_element('outcomes_definition');
394         $outcome = new backup_nested_element('outcome', array('id'), array(
395             'courseid', 'userid', 'shortname', 'fullname',
396             'scaleid', 'description', 'descriptionformat', 'timecreated',
397             'timemodified','usermodified'));
399         // Build the tree
401         $outcomesdef->add_child($outcome);
403         // Define sources
405         $outcome->set_source_sql("SELECT o.*
406                                     FROM {grade_outcomes} o
407                                     JOIN {backup_ids_temp} bi ON o.id = bi.itemid
408                                    WHERE bi.backupid = ?
409                                      AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
411         // Return main element (outcomesdef)
412         return $outcomesdef;
413     }
416 /**
417  * structure step in charge of constructing the filters.xml file for all the filters found
418  * in activity
419  */
420 class backup_filters_structure_step extends backup_structure_step {
422     protected function define_structure() {
424         // Define each element separated
426         $filters = new backup_nested_element('filters');
428         $actives = new backup_nested_element('filter_actives');
430         $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
432         $configs = new backup_nested_element('filter_configs');
434         $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
436         // Build the tree
438         $filters->add_child($actives);
439         $filters->add_child($configs);
441         $actives->add_child($active);
442         $configs->add_child($config);
444         // Define sources
446         list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
448         $active->set_source_array($activearr);
449         $config->set_source_array($configarr);
451         // Return the root element (filters)
452         return $filters;
453     }
456 /**
457  * structure step in charge of constructing the comments.xml file for all the comments found
458  * in a given context
459  */
460 class backup_comments_structure_step extends backup_structure_step {
462     protected function define_structure() {
464         // Define each element separated
466         $comments = new backup_nested_element('comments');
468         $comment = new backup_nested_element('comment', array('id'), array(
469             'commentarea', 'itemid', 'content', 'format',
470             'userid', 'timecreated'));
472         // Build the tree
474         $comments->add_child($comment);
476         // Define sources
478         $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
480         // Define id annotations
482         $comment->annotate_ids('user', 'userid');
484         // Return the root element (comments)
485         return $comments;
486     }
489 /**
490  * structure step in charge if constructing the completion.xml file for all the users completion
491  * information in a given activity
492  */
493 class backup_userscompletion_structure_step extends backup_structure_step {
495     protected function define_structure() {
497         // Define each element separated
499         $completions = new backup_nested_element('completions');
501         $completion = new backup_nested_element('completion', array('id'), array(
502             'userid', 'completionstate', 'viewed', 'timemodified'));
504         // Build the tree
506         $completions->add_child($completion);
508         // Define sources
510         $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
512         // Define id annotations
514         $completion->annotate_ids('user', 'userid');
516         // Return the root element (completions)
517         return $completions;
518     }
521 /**
522  * structure step in charge of constructing the main groups.xml file for all the groups and
523  * groupings information already annotated
524  */
525 class backup_groups_structure_step extends backup_structure_step {
527     protected function define_structure() {
529         // To know if we are including users
530         $users = $this->get_setting_value('users');
532         // Define each element separated
534         $groups = new backup_nested_element('groups');
536         $group = new backup_nested_element('group', array('id'), array(
537             'name', 'description', 'descriptionformat', 'enrolmentkey',
538             'picture', 'hidepicture', 'timecreated', 'timemodified'));
540         $members = new backup_nested_element('group_members');
542         $member = new backup_nested_element('group_member', array('id'), array(
543             'userid', 'timeadded'));
545         $groupings = new backup_nested_element('groupings');
547         $grouping = new backup_nested_element('grouping', 'id', array(
548             'name', 'description', 'descriptionformat', 'configdata',
549             'timecreated', 'timemodified'));
551         $groupinggroups = new backup_nested_element('grouping_groups');
553         $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
554             'groupid', 'timeadded'));
556         // Build the tree
558         $groups->add_child($group);
559         $groups->add_child($groupings);
561         $group->add_child($members);
562         $members->add_child($member);
564         $groupings->add_child($grouping);
565         $grouping->add_child($groupinggroups);
566         $groupinggroups->add_child($groupinggroup);
568         // Define sources
570         $group->set_source_sql("
571             SELECT g.*
572               FROM {groups} g
573               JOIN {backup_ids_temp} bi ON g.id = bi.itemid
574              WHERE bi.backupid = ?
575                AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
577         // This only happens if we are including users
578         if ($users) {
579             $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
580         }
582         $grouping->set_source_sql("
583             SELECT g.*
584               FROM {groupings} g
585               JOIN {backup_ids_temp} bi ON g.id = bi.itemid
586              WHERE bi.backupid = ?
587                AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
589         $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
591         // Define id annotations (as final)
593         $member->annotate_ids('userfinal', 'userid');
595         // Define file annotations
597         // TODO: Change "course_group_image" file area to the one finally used for group images
598         $group->annotate_files(array('course_group_description', 'course_group_image'), 'id');
600         // Return the root element (groups)
601         return $groups;
602     }
605 /**
606  * structure step in charge of constructing the main users.xml file for all the users already
607  * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
608  * overrides.
609  */
610 class backup_users_structure_step extends backup_structure_step {
612     protected function define_structure() {
613         global $CFG;
615         // To know if we are anonymizing users
616         $anonymize = $this->get_setting_value('anonymize');
617         // To know if we are including role assignments
618         $roleassignments = $this->get_setting_value('role_assignments');
620         // Define each element separated
622         $users = new backup_nested_element('users');
624         // Create the array of user fields by hand, as far as we have various bits to control
625         // anonymize option, password backup, mnethostid...
627         // First, the fields not needing anonymization nor special handling
628         $normalfields = array(
629             'confirmed', 'policyagreed', 'deleted',
630             'lang', 'theme', 'timezone', 'firstaccess',
631             'lastaccess', 'lastlogin', 'currentlogin', 'secret',
632             'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
633             'ajax', 'autosubscribe', 'trackforums', 'timecreated',
634             'timemodified', 'trustbitmask', 'screenreader');
636         // Then, the fields potentially needing anonymization
637         $anonfields = array(
638             'username', 'idnumber', 'firstname', 'lastname',
639             'email', 'emailstop', 'lastip', 'picture',
640             'url', 'description', 'description_format', 'imagealt', 'auth');
642         // Add anonymized fields to $userfields with custom final element
643         foreach ($anonfields as $field) {
644             if ($anonymize) {
645                 $userfields[] = new anonymizer_final_element($field);
646             } else {
647                 $userfields[] = $field; // No anonymization, normally added
648             }
649         }
651         // mnethosturl requires special handling (custom final element)
652         $userfields[] = new mnethosturl_final_element('mnethosturl');
654         // password added conditionally
655         if (!empty($CFG->includeuserpasswordsinbackup)) {
656             $userfields[] = 'password';
657         }
659         // Merge all the fields
660         $userfields = array_merge($userfields, $normalfields);
662         $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
664         $customfields = new backup_nested_element('custom_fields');
666         $customfield = new backup_nested_element('custom_field', array('id'), array(
667             'field_name', 'field_type', 'field_data'));
669         $tags = new backup_nested_element('tags');
671         $tag = new backup_nested_element('tag', array('id'), array(
672             'name', 'rawname'));
674         $preferences = new backup_nested_element('preferences');
676         $preference = new backup_nested_element('preference', array('id'), array(
677             'name', 'value'));
679         $roles = new backup_nested_element('roles');
681         $overrides = new backup_nested_element('role_overrides');
683         $override = new backup_nested_element('override', array('id'), array(
684             'roleid', 'capability', 'permission', 'timemodified',
685             'modifierid'));
687         $assignments = new backup_nested_element('role_assignments');
689         $assignment = new backup_nested_element('assignment', array('id'), array(
690             'roleid', 'userid', 'hidden', 'timestart',
691             'timeend', 'timemodified', 'modifierid', 'enrol',
692             'sortorder'));
694         // Build the tree
696         $users->add_child($user);
698         $user->add_child($customfields);
699         $customfields->add_child($customfield);
701         $user->add_child($tags);
702         $tags->add_child($tag);
704         $user->add_child($preferences);
705         $preferences->add_child($preference);
707         $user->add_child($roles);
709         $roles->add_child($overrides);
710         $roles->add_child($assignments);
712         $overrides->add_child($override);
713         $assignments->add_child($assignment);
715         // Define sources
717         $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
718                                  FROM {user} u
719                                  JOIN {backup_ids_temp} bi ON bi.itemid = u.id
720                                  JOIN {context} c ON c.instanceid = u.id
721                             LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
722                                 WHERE bi.backupid = ?
723                                   AND bi.itemname = ?
724                                   AND c.contextlevel = ?', array(
725                                       $this->is_sqlparam($this->get_backupid()),
726                                       $this->is_sqlparam('userfinal'),
727                                       $this->is_sqlparam(CONTEXT_USER)));
729         // All the rest on information is only added if we arent
730         // in an anonymized backup
731         if (!$anonymize) {
732             $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
733                                             FROM {user_info_field} f
734                                             JOIN {user_info_data} d ON d.fieldid = f.id
735                                            WHERE d.userid = ?', array(backup::VAR_PARENTID));
737             $customfield->set_source_alias('shortname', 'field_name');
738             $customfield->set_source_alias('datatype',  'field_type');
739             $customfield->set_source_alias('data',      'field_data');
741             $tag->set_source_sql('SELECT t.id, t.name, t.rawname
742                                     FROM {tag} t
743                                     JOIN {tag_instance} ti ON ti.tagid = t.id
744                                    WHERE ti.itemtype = ?
745                                      AND ti.itemid = ?', array(
746                                          $this->is_sqlparam('user'),
747                                          backup::VAR_PARENTID));
749             $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
751             $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
753             // Assignments only added if specified
754             if ($roleassignments) {
755                 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
756             }
758             // Define id annotations (as final)
759             $override->annotate_ids('rolefinal', 'roleid');
760         }
762         // Return root element (users)
763         return $users;
764     }
767 /**
768  * structure step in charge of constructing the block.xml file for one
769  * given block (intance and positions). If the block has custom DB structure
770  * that will go to a separate file (different step defined in block class)
771  */
772 class backup_block_instance_structure_step extends backup_structure_step {
774     protected function define_structure() {
775         global $DB;
777         // Define each element separated
779         $block = new backup_nested_element('block', array('id', 'version'), array(
780             'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
781             'defaultregion', 'defaultweight', 'configdata'));
783         $positions = new backup_nested_element('block_positions', null, array(
784             'contextid', 'pagetype', 'subpage', 'visible',
785             'region', 'weight'));
787         // Build the tree
789         $block->add_child($positions);
791         // Transform configdata information if needed (process links and friends)
792         $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
793         if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
794             $configdata = (array)unserialize(base64_decode($blockrec->configdata));
795             foreach ($configdata as $attribute => $value) {
796                 if (in_array($attribute, $attrstotransform)) {
797                     $configdata[$attribute] = $this->contenttransformer->process($value);
798                 }
799             }
800             $blockrec->configdata = base64_encode(serialize((object)$configdata));
801         }
802         // Get the version of the block
803         $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
805         // Define sources
807         $block->set_source_array(array($blockrec));
809         $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
811         // Return the root element (block)
812         return $block;
813     }
816 /**
817  * structure step in charge of constructing the logs.xml file for all the log records found
818  * in activity
819  */
820 class backup_activity_logs_structure_step extends backup_structure_step {
822     protected function define_structure() {
824         // Define each element separated
826         $logs = new backup_nested_element('logs');
828         $log = new backup_nested_element('log', array('id'), array(
829             'time', 'userid', 'ip', 'module',
830             'action', 'url', 'info'));
832         // Build the tree
834         $logs->add_child($log);
836         // Define sources
838         $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
840         // Annotations
841         // NOTE: We don't annotate users from logs as far as they MUST be
842         //       always annotated by the activity.
844         // Return the root element (logs)
846         return $logs;
847     }
850 /**
851  * structure in charge of constructing the inforef.xml file for all the items we want
852  * to have referenced there (users, roles, files...)
853  */
854 class backup_inforef_structure_step extends backup_structure_step {
856     protected function define_structure() {
858         // Items we want to include in the inforef file. NOTE: Important to keep this
859         // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
860         $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
862         // Build the tree
864         $inforef = new backup_nested_element('inforef');
866         // For each item, conditionally, if there are already records, build element
867         foreach ($items as $itemname) {
868             if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
869                 $elementroot = new backup_nested_element($itemname . 'ref');
870                 $element = new backup_nested_element($itemname, array('id'));
871                 $inforef->add_child($elementroot);
872                 $elementroot->add_child($element);
873                 $element->set_source_sql("
874                     SELECT itemid AS id
875                      FROM {backup_ids_temp}
876                     WHERE backupid = ?
877                       AND itemname = ?",
878                    array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
879             }
880         }
882         // We don't annotate anything there, but rely in the next step
883         // (move_inforef_annotations_to_final) that will change all the
884         // already saved 'inforref' entries to their 'final' annotations.
885         return $inforef;
886     }
889 /**
890  * This step will get all the annotations already processed to inforef.xml file and
891  * transform them into 'final' annotations.
892  */
893 class move_inforef_annotations_to_final extends backup_execution_step {
895     protected function define_execution() {
897         // Items we want to include in the inforef file. NOTE: Important to keep this
898         // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
899         $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
900         foreach ($items as $itemname) {
901             // Delegate to dbops
902             backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
903         }
904     }
907 /**
908  * structure in charge of constructing the files.xml file with all the
909  * annotated (final) files along the process. At, the same time, and
910  * using one specialised nested_element, will copy them form moodle storage
911  * to backup storage
912  */
913 class backup_final_files_structure_step extends backup_structure_step {
915     protected function define_structure() {
917         // Define elements
919         $files = new backup_nested_element('files');
921         $file = new file_nested_element('file', array('id'), array(
922             'contenthash', 'contextid', 'filearea', 'itemid',
923             'filepath', 'filename', 'userid', 'filesize',
924             'mimetype', 'status', 'timecreated', 'timemodified',
925             'source', 'author', 'license'));
927         // Build the tree
929         $files->add_child($file);
931         // Define sources
933         $file->set_source_sql("SELECT f.*
934                                  FROM {files} f
935                                  JOIN {backup_ids_temp} bi ON f.id = bi.itemid
936                                 WHERE bi.backupid = ?
937                                   AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
939         return $files;
940     }
943 /**
944  * Structure step in charge of creating the main moodle_backup.xml file
945  * where all the information related to the backup, settings, license and
946  * other information needed on restore is added*/
947 class backup_main_structure_step extends backup_structure_step {
949     protected function define_structure() {
951         global $CFG;
953         $info = array();
955         $info['name'] = $this->get_setting_value('filename');
956         $info['moodle_version'] = $CFG->version;
957         $info['moodle_release'] = $CFG->release;
958         $info['backup_version'] = $CFG->backup_version;
959         $info['backup_release'] = $CFG->backup_release;
960         $info['backup_date']    = time();
961         $info['backup_uniqueid']= $this->get_backupid();
962         $info['original_wwwroot']=$CFG->wwwroot;
963         $info['original_site_identifier'] = get_site_identifier();
964         $info['original_course_id'] = $this->get_courseid();
966         // Get more information from controller
967         list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
969         // Define elements
971         $moodle_backup = new backup_nested_element('moodle_backup');
973         $information = new backup_nested_element('information', null, array(
974             'name', 'moodle_version', 'moodle_release', 'backup_version',
975             'backup_release', 'backup_date', 'original_wwwroot',
976             'original_site_identifier', 'original_course_id'));
978         $details = new backup_nested_element('details');
980         $detail = new backup_nested_element('detail', array('backup_id'), array(
981             'type', 'format', 'interactive', 'mode',
982             'execution', 'executiontime'));
984         $contents = new backup_nested_element('contents');
986         $activities = new backup_nested_element('activities');
988         $activity = new backup_nested_element('activity', null, array(
989             'moduleid', 'sectionid', 'modulename', 'title',
990             'directory'));
992         $sections = new backup_nested_element('sections');
994         $section = new backup_nested_element('section', null, array(
995             'sectionid', 'title', 'directory'));
997         $course = new backup_nested_element('course', null, array(
998             'courseid', 'title', 'directory'));
1000         $settings = new backup_nested_element('settings');
1002         $setting = new backup_nested_element('setting', null, array(
1003             'level', 'activity', 'name', 'value'));
1005         // Build the tree
1007         $moodle_backup->add_child($information);
1009         $information->add_child($details);
1010         $details->add_child($detail);
1012         $information->add_child($contents);
1013         if (!empty($cinfo['activities'])) {
1014             $contents->add_child($activities);
1015             $activities->add_child($activity);
1016         }
1017         if (!empty($cinfo['sections'])) {
1018             $contents->add_child($sections);
1019             $sections->add_child($section);
1020         }
1021         if (!empty($cinfo['course'])) {
1022             $contents->add_child($course);
1023         }
1025         $information->add_child($settings);
1026         $settings->add_child($setting);
1029         // Set the sources
1031         $information->set_source_array(array((object)$info));
1033         $detail->set_source_array($dinfo);
1035         $activity->set_source_array($cinfo['activities']);
1037         $section->set_source_array($cinfo['sections']);
1039         $course->set_source_array($cinfo['course']);
1041         $setting->set_source_array($sinfo);
1043         // Prepare some information to be sent to main moodle_backup.xml file
1044         return $moodle_backup;
1045     }
1049 /**
1050  * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1051  * and put them to the backup_ids tables, to be used later as base to backup them
1052  */
1053 class backup_activity_grade_items_to_ids extends backup_execution_step {
1055     protected function define_execution() {
1057         // Fetch all activity grade items
1058         if ($items = grade_item::fetch_all(array(
1059                          'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1060                          'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1061             // Annotate them in backup_ids
1062             foreach ($items as $item) {
1063                 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1064             }
1065         }
1066     }
1069 /**
1070  * This step will annotate all the groups belonging to already annotated groupings
1071  */
1072 class backup_annotate_groups_from_groupings extends backup_execution_step {
1074     protected function define_execution() {
1075         global $DB;
1077         // Fetch all the annotated groupings
1078         if ($groupings = $DB->get_records('backup_ids_temp', array(
1079                 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1080             foreach ($groupings as $grouping) {
1081                 if ($groups = $DB->get_records('groupings_groups', array(
1082                         'groupingid' => $grouping->itemid))) {
1083                     foreach ($groups as $group) {
1084                         backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1085                     }
1086                 }
1087             }
1088         }
1089     }
1092 /**
1093  * This step will annotate all the scales belonging to already annotated outcomes
1094  */
1095 class backup_annotate_scales_from_outcomes extends backup_execution_step {
1097     protected function define_execution() {
1098         global $DB;
1100         // Fetch all the annotated outcomes
1101         if ($outcomes = $DB->get_records('backup_ids_temp', array(
1102                 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1103             foreach ($outcomes as $outcome) {
1104                 if ($scale = $DB->get_record('grade_outcomes', array(
1105                         'id' => $outcome->itemid))) {
1106                     // Annotate as scalefinal because it's > 0
1107                     backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1108                 }
1109             }
1110         }
1111     }
1114 /**
1115  * This step will generate all the user annotations for the already
1116  * annottated (final) users. Need to do this here because each user
1117  * has its own context and structure tasks only are able to handle
1118  * one context. Also, this step will guarantee that every user has
1119  * its context created (req for other steps)
1120  */
1121 class backup_annotate_all_user_files extends backup_execution_step {
1123     protected function define_execution() {
1124         global $DB;
1126         // List of fileareas we are going to annotate
1127         // TODO: Change "user_image" file area to the one finally used for user images
1128         $fileareas = array(
1129             'user_private', 'user_profile', 'user_image');
1131         // Fetch all annotated (final) users
1132         $rs = $DB->get_recordset('backup_ids_temp', array(
1133             'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1134         foreach ($rs as $record) {
1135             $userid = $record->itemid;
1136             $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1137             // Proceed with every user filearea
1138             foreach ($fileareas as $filearea) {
1139                 // We don't need to specify itemid ($userid - 4th param) as far as by
1140                 // context we can get all the associated files. See MDL-22092
1141                 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, $filearea, null);
1142             }
1143         }
1144         $rs->close();
1145     }
1148 /**
1149  * structure step in charge of constructing the grades.xml file for all the grade items
1150  * and letters related to one activity
1151  */
1152 class backup_activity_grades_structure_step extends backup_structure_step {
1154     protected function define_structure() {
1156         // To know if we are including userinfo
1157         $userinfo = $this->get_setting_value('userinfo');
1159         // Define each element separated
1161         $book = new backup_nested_element('activity_gradebook');
1163         $items = new backup_nested_element('grade_items');
1165         $item = new backup_nested_element('grade_item', array('id'), array(
1166             'categoryid', 'itemname', 'itemtype', 'itemmodule',
1167             'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1168             'calculation', 'gradetype', 'grademax', 'grademin',
1169             'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1170             'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1171             'decimals', 'hidden', 'locked', 'locktime',
1172             'needsupdate', 'timecreated', 'timemodified'));
1174         $grades = new backup_nested_element('grade_grades');
1176         $grade = new backup_nested_element('grade_grade', array('id'), array(
1177             'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1178             'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1179             'locked', 'locktime', 'exported', 'overridden',
1180             'excluded', 'feedback', 'feedbackformat', 'information',
1181             'informationformat', 'timecreated', 'timemodified'));
1183         $letters = new backup_nested_element('grade_letters');
1185         $letter = new backup_nested_element('grade_letter', 'id', array(
1186             'lowerboundary', 'letter'));
1188         // Build the tree
1190         $book->add_child($items);
1191         $items->add_child($item);
1193         $item->add_child($grades);
1194         $grades->add_child($grade);
1196         $book->add_child($letters);
1197         $letters->add_child($letter);
1199         // Define sources
1201         $item->set_source_sql("
1202             SELECT gi.*
1203               FROM {grade_items} gi
1204               JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1205              WHERE bi.backupid = ?
1206                AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1208         // This only happens if we are including user info
1209         if ($userinfo) {
1210             $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1211         }
1213         $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1215         // Annotations
1217         $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1218         $item->annotate_ids('outcome', 'outcomeid');
1220         $grade->annotate_ids('user', 'userid');
1221         $grade->annotate_ids('user', 'usermodified');
1223         // Return the root element (book)
1225         return $book;
1226     }