MDL-21432 backup 2.0 - initial commit. moodle2 format
[moodle.git] / backup / moodle2 / backup_stepslib.php
CommitLineData
77547b46
EL
1<?php
2
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/>.
17
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 */
24
25/**
26 * Define all the backup steps that will be used by common tasks in backup
27 */
28class create_and_clean_temp_stuff extends backup_execution_step {
29
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}
37
38/**
39 * Create the directory where all the task (activity/block...) information will be stored
40 */
41class create_taskbasepath_directory extends backup_execution_step {
42
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}
51
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 */
56abstract class backup_activity_structure_step extends backup_structure_step {
57
58 protected function prepare_activity_structure($activitystructure) {
59
60 // Create the wrap element
61 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
62
63 // Build the tree
64 $activity->add_child($activitystructure);
65
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()));
72
73 $activity->set_source_array($activityarr);
74
75 // Return the root element (activity)
76 return $activity;
77 }
78}
79
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 */
84abstract class backup_block_structure_step extends backup_structure_step {
85
86 protected function prepare_block_structure($blockstructure) {
87
88 // Create the wrap element
89 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
90
91 // Build the tree
92 $block->add_child($blockstructure);
93
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()));
99
100 $block->set_source_array($blockarr);
101
102 // Return the root element (block)
103 return $block;
104 }
105}
106
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 */
112class backup_module_structure_step extends backup_structure_step {
113
114 protected function define_structure() {
115
116 // Define each element separated
117
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'));
124
125 $availinfo = new backup_nested_element('availability_info');
126 $availability = new backup_nested_element('availability', array('id'), array(
127 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
128
129 // Define the tree
130 $module->add_child($availinfo);
131 $availinfo->add_child($availability);
132
133 // Set the sources
134
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));
141
142 $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
143
144 // Define annotations
145 $module->annotate_ids('grouping', 'groupingid');
146
147 // Return the root element ($module)
148 return $module;
149 }
150}
151
152/**
153 * structure step that will genereate the section.xml file for the section
154 * annotating files
155 */
156class backup_section_structure_step extends backup_structure_step {
157
158 protected function define_structure() {
159
160 // Define each element separated
161
162 $section = new backup_nested_element('section', array('id'), array(
163 'number', 'summary', 'sequence', 'visible'));
164
165 // Define sources
166
167 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
168
169 // Set annotations
170 $section->annotate_files(array('course_section'), 'id');
171
172 return $section;
173 }
174}
175
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 */
181class backup_course_structure_step extends backup_structure_step {
182
183 protected function define_structure() {
184 global $DB;
185
186 // Define each element separated
187
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'));
199
200 $category = new backup_nested_element('category', array('id'), array(
201 'name', 'description'));
202
203 $tags = new backup_nested_element('tags');
204
205 $tag = new backup_nested_element('tag', array('id'), array(
206 'name', 'rawname'));
207
208 $allowedmodules = new backup_nested_element('allowed_modules');
209
210 $module = new backup_nested_element('module', array('modulename'));
211
212 // Build the tree
213
214 $course->add_child($category);
215
216 $course->add_child($tags);
217 $tags->add_child($tag);
218
219 $course->add_child($allowedmodules);
220 $allowedmodules->add_child($module);
221
222 // Set the sources
223
224 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
225 $courserec->contextid = $this->task->get_contextid();
226
227 $course->set_source_array(array($courserec));
228
229 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
230
231 $category->set_source_array(array($categoryrec));
232
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));
240
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));
245
246 // Some annotations
247
248 $course->annotate_ids('role', 'defaultrole');
249 $course->annotate_ids('grouping', 'defaultgroupingid');
250
251 $course->annotate_files(array('course_summary', 'course_content'), null);
252
253 // Return root element ($course)
254
255 return $course;
256 }
257}
258
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 */
263class backup_roles_structure_step extends backup_structure_step {
264
265 protected function define_structure() {
266
267 // To know if we are including role assignments
268 $roleassignments = $this->get_setting_value('role_assignments');
269
270 // Define each element separated
271
272 $roles = new backup_nested_element('roles');
273
274 $overrides = new backup_nested_element('role_overrides');
275
276 $override = new backup_nested_element('override', array('id'), array(
277 'roleid', 'capability', 'permission', 'timemodified',
278 'modifierid'));
279
280 $assignments = new backup_nested_element('role_assignments');
281
282 $assignment = new backup_nested_element('assignment', array('id'), array(
283 'roleid', 'userid', 'hidden', 'timestart',
284 'timeend', 'timemodified', 'modifierid', 'enrol',
285 'sortorder'));
286
287 // Build the tree
288 $roles->add_child($overrides);
289 $roles->add_child($assignments);
290
291 $overrides->add_child($override);
292 $assignments->add_child($assignment);
293
294 // Define sources
295
296 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
297
298 // Assignments only added if specified
299 if ($roleassignments) {
300 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
301 }
302
303 // Define id annotations
304 $override->annotate_ids('role', 'roleid');
305
306 $assignment->annotate_ids('role', 'roleid');
307
308 $assignment->annotate_ids('user', 'userid');
309
310 return $roles;
311 }
312}
313
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 */
319class backup_final_roles_structure_step extends backup_structure_step {
320
321 protected function define_structure() {
322
323 // Define elements
324
325 $rolesdef = new backup_nested_element('roles_definition');
326
327 $role = new backup_nested_element('role', array('id'), array(
328 'name', 'shortname', 'nameincourse', 'description',
329 'sortorder', 'archetype'));
330
331 // Build the tree
332
333 $rolesdef->add_child($role);
334
335 // Define sources
336
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));
343
344 // Return main element (rolesdef)
345 return $rolesdef;
346 }
347}
348
349/**
350 * structure step that will generate the scales.xml containing the
351 * list of scales used along the whole backup process.
352 */
353class backup_final_scales_structure_step extends backup_structure_step {
354
355 protected function define_structure() {
356
357 // Define elements
358
359 $scalesdef = new backup_nested_element('scales_definition');
360
361 $scale = new backup_nested_element('scale', array('id'), array(
362 'courseid', 'userid', 'name', 'scale',
363 'description', 'descriptionformat', 'timemodified'));
364
365 // Build the tree
366
367 $scalesdef->add_child($scale);
368
369 // Define sources
370
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));
376
377 // Return main element (scalesdef)
378 return $scalesdef;
379 }
380}
381
382/**
383 * structure step that will generate the outcomes.xml containing the
384 * list of outcomes used along the whole backup process.
385 */
386class backup_final_outcomes_structure_step extends backup_structure_step {
387
388 protected function define_structure() {
389
390 // Define elements
391
392 $outcomesdef = new backup_nested_element('outcomes_definition');
393
394 $outcome = new backup_nested_element('outcome', array('id'), array(
395 'courseid', 'userid', 'shortname', 'fullname',
396 'scaleid', 'description', 'descriptionformat', 'timecreated',
397 'timemodified','usermodified'));
398
399 // Build the tree
400
401 $outcomesdef->add_child($outcome);
402
403 // Define sources
404
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));
410
411 // Return main element (outcomesdef)
412 return $outcomesdef;
413 }
414}
415
416/**
417 * structure step in charge of constructing the filters.xml file for all the filters found
418 * in activity
419 */
420class backup_filters_structure_step extends backup_structure_step {
421
422 protected function define_structure() {
423
424 // Define each element separated
425
426 $filters = new backup_nested_element('filters');
427
428 $actives = new backup_nested_element('filter_actives');
429
430 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
431
432 $configs = new backup_nested_element('filter_configs');
433
434 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
435
436 // Build the tree
437
438 $filters->add_child($actives);
439 $filters->add_child($configs);
440
441 $actives->add_child($active);
442 $configs->add_child($config);
443
444 // Define sources
445
446 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
447
448 $active->set_source_array($activearr);
449 $config->set_source_array($configarr);
450
451 // Return the root element (filters)
452 return $filters;
453 }
454}
455
456/**
457 * structure step in charge of constructing the comments.xml file for all the comments found
458 * in a given context
459 */
460class backup_comments_structure_step extends backup_structure_step {
461
462 protected function define_structure() {
463
464 // Define each element separated
465
466 $comments = new backup_nested_element('comments');
467
468 $comment = new backup_nested_element('comment', array('id'), array(
469 'commentarea', 'itemid', 'content', 'format',
470 'userid', 'timecreated'));
471
472 // Build the tree
473
474 $comments->add_child($comment);
475
476 // Define sources
477
478 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
479
480 // Define id annotations
481
482 $comment->annotate_ids('user', 'userid');
483
484 // Return the root element (comments)
485 return $comments;
486 }
487}
488
489/**
490 * structure step in charge if constructing the completion.xml file for all the users completion
491 * information in a given activity
492 */
493class backup_userscompletion_structure_step extends backup_structure_step {
494
495 protected function define_structure() {
496
497 // Define each element separated
498
499 $completions = new backup_nested_element('completions');
500
501 $completion = new backup_nested_element('completion', array('id'), array(
502 'userid', 'completionstate', 'viewed', 'timemodified'));
503
504 // Build the tree
505
506 $completions->add_child($completion);
507
508 // Define sources
509
510 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
511
512 // Define id annotations
513
514 $completion->annotate_ids('user', 'userid');
515
516 // Return the root element (completions)
517 return $completions;
518 }
519}
520
521/**
522 * structure step in charge of constructing the main groups.xml file for all the groups and
523 * groupings information already annotated
524 */
525class backup_groups_structure_step extends backup_structure_step {
526
527 protected function define_structure() {
528
529 // To know if we are including users
530 $users = $this->get_setting_value('users');
531
532 // Define each element separated
533
534 $groups = new backup_nested_element('groups');
535
536 $group = new backup_nested_element('group', array('id'), array(
537 'name', 'description', 'descriptionformat', 'enrolmentkey',
538 'picture', 'hidepicture', 'timecreated', 'timemodified'));
539
540 $members = new backup_nested_element('group_members');
541
542 $member = new backup_nested_element('group_member', array('id'), array(
543 'userid', 'timeadded'));
544
545 $groupings = new backup_nested_element('groupings');
546
547 $grouping = new backup_nested_element('grouping', 'id', array(
548 'name', 'description', 'descriptionformat', 'configdata',
549 'timecreated', 'timemodified'));
550
551 $groupinggroups = new backup_nested_element('grouping_groups');
552
553 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
554 'groupid', 'timeadded'));
555
556 // Build the tree
557
558 $groups->add_child($group);
559 $groups->add_child($groupings);
560
561 $group->add_child($members);
562 $members->add_child($member);
563
564 $groupings->add_child($grouping);
565 $grouping->add_child($groupinggroups);
566 $groupinggroups->add_child($groupinggroup);
567
568 // Define sources
569
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));
576
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 }
581
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));
588
589 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
590
591 // Define id annotations (as final)
592
593 $member->annotate_ids('userfinal', 'userid');
594
595 // Define file annotations
596
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');
599
600 // Return the root element (groups)
601 return $groups;
602 }
603}
604
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 */
610class backup_users_structure_step extends backup_structure_step {
611
612 protected function define_structure() {
613 global $CFG;
614
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');
619
620 // Define each element separated
621
622 $users = new backup_nested_element('users');
623
624 // Create the array of user fields by hand, as far as we have various bits to control
625 // anonymize option, password backup, mnethostid...
626
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');
635
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');
641
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 }
650
651 // mnethosturl requires special handling (custom final element)
652 $userfields[] = new mnethosturl_final_element('mnethosturl');
653
654 // password added conditionally
655 if (!empty($CFG->includeuserpasswordsinbackup)) {
656 $userfields[] = 'password';
657 }
658
659 // Merge all the fields
660 $userfields = array_merge($userfields, $normalfields);
661
662 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
663
664 $customfields = new backup_nested_element('custom_fields');
665
666 $customfield = new backup_nested_element('custom_field', array('id'), array(
667 'field_name', 'field_type', 'field_data'));
668
669 $tags = new backup_nested_element('tags');
670
671 $tag = new backup_nested_element('tag', array('id'), array(
672 'name', 'rawname'));
673
674 $preferences = new backup_nested_element('preferences');
675
676 $preference = new backup_nested_element('preference', array('id'), array(
677 'name', 'value'));
678
679 $roles = new backup_nested_element('roles');
680
681 $overrides = new backup_nested_element('role_overrides');
682
683 $override = new backup_nested_element('override', array('id'), array(
684 'roleid', 'capability', 'permission', 'timemodified',
685 'modifierid'));
686
687 $assignments = new backup_nested_element('role_assignments');
688
689 $assignment = new backup_nested_element('assignment', array('id'), array(
690 'roleid', 'userid', 'hidden', 'timestart',
691 'timeend', 'timemodified', 'modifierid', 'enrol',
692 'sortorder'));
693
694 // Build the tree
695
696 $users->add_child($user);
697
698 $user->add_child($customfields);
699 $customfields->add_child($customfield);
700
701 $user->add_child($tags);
702 $tags->add_child($tag);
703
704 $user->add_child($preferences);
705 $preferences->add_child($preference);
706
707 $user->add_child($roles);
708
709 $roles->add_child($overrides);
710 $roles->add_child($assignments);
711
712 $overrides->add_child($override);
713 $assignments->add_child($assignment);
714
715 // Define sources
716
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)));
728
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));
736
737 $customfield->set_source_alias('shortname', 'field_name');
738 $customfield->set_source_alias('datatype', 'field_type');
739 $customfield->set_source_alias('data', 'field_data');
740
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));
748
749 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
750
751 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
752
753 // Assignments only added if specified
754 if ($roleassignments) {
755 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
756 }
757
758 // Define id annotations (as final)
759 $override->annotate_ids('rolefinal', 'roleid');
760 }
761
762 // Return root element (users)
763 return $users;
764 }
765}
766
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 */
772class backup_block_instance_structure_step extends backup_structure_step {
773
774 protected function define_structure() {
775 global $DB;
776
777 // Define each element separated
778
779 $block = new backup_nested_element('block', array('id', 'version'), array(
780 'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
781 'defaultregion', 'defaultweight', 'configdata'));
782
783 $positions = new backup_nested_element('block_positions', null, array(
784 'contextid', 'pagetype', 'subpage', 'visible',
785 'region', 'weight'));
786
787 // Build the tree
788
789 $block->add_child($positions);
790
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()));
804
805 // Define sources
806
807 $block->set_source_array(array($blockrec));
808
809 $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
810
811 // Return the root element (block)
812 return $block;
813 }
814}
815
816/**
817 * structure step in charge of constructing the logs.xml file for all the log records found
818 * in activity
819 */
820class backup_activity_logs_structure_step extends backup_structure_step {
821
822 protected function define_structure() {
823
824 // Define each element separated
825
826 $logs = new backup_nested_element('logs');
827
828 $log = new backup_nested_element('log', array('id'), array(
829 'time', 'userid', 'ip', 'module',
830 'action', 'url', 'info'));
831
832 // Build the tree
833
834 $logs->add_child($log);
835
836 // Define sources
837
838 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
839
840 // Annotations
841 // NOTE: We don't annotate users from logs as far as they MUST be
842 // always annotated by the activity.
843
844 // Return the root element (logs)
845
846 return $logs;
847 }
848}
849
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 */
854class backup_inforef_structure_step extends backup_structure_step {
855
856 protected function define_structure() {
857
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');
861
862 // Build the tree
863
864 $inforef = new backup_nested_element('inforef');
865
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 }
881
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 }
887}
888
889/**
890 * This step will get all the annotations already processed to inforef.xml file and
891 * transform them into 'final' annotations.
892 */
893class move_inforef_annotations_to_final extends backup_execution_step {
894
895 protected function define_execution() {
896
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 }
905}
906
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 */
913class backup_final_files_structure_step extends backup_structure_step {
914
915 protected function define_structure() {
916
917 // Define elements
918
919 $files = new backup_nested_element('files');
920
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'));
926
927 // Build the tree
928
929 $files->add_child($file);
930
931 // Define sources
932
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));
938
939 return $files;
940 }
941}
942
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*/
947class backup_main_structure_step extends backup_structure_step {
948
949 protected function define_structure() {
950
951 global $CFG;
952
953 $info = array();
954
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();
965
966 // Get more information from controller
967 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
968
969 // Define elements
970
971 $moodle_backup = new backup_nested_element('moodle_backup');
972
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'));
977
978 $details = new backup_nested_element('details');
979
980 $detail = new backup_nested_element('detail', array('backup_id'), array(
981 'type', 'format', 'interactive', 'mode',
982 'execution', 'executiontime'));
983
984 $contents = new backup_nested_element('contents');
985
986 $activities = new backup_nested_element('activities');
987
988 $activity = new backup_nested_element('activity', null, array(
989 'moduleid', 'sectionid', 'modulename', 'title',
990 'directory'));
991
992 $sections = new backup_nested_element('sections');
993
994 $section = new backup_nested_element('section', null, array(
995 'sectionid', 'title', 'directory'));
996
997 $course = new backup_nested_element('course', null, array(
998 'courseid', 'title', 'directory'));
999
1000 $settings = new backup_nested_element('settings');
1001
1002 $setting = new backup_nested_element('setting', null, array(
1003 'level', 'activity', 'name', 'value'));
1004
1005 // Build the tree
1006
1007 $moodle_backup->add_child($information);
1008
1009 $information->add_child($details);
1010 $details->add_child($detail);
1011
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 }
1024
1025 $information->add_child($settings);
1026 $settings->add_child($setting);
1027
1028
1029 // Set the sources
1030
1031 $information->set_source_array(array((object)$info));
1032
1033 $detail->set_source_array($dinfo);
1034
1035 $activity->set_source_array($cinfo['activities']);
1036
1037 $section->set_source_array($cinfo['sections']);
1038
1039 $course->set_source_array($cinfo['course']);
1040
1041 $setting->set_source_array($sinfo);
1042
1043 // Prepare some information to be sent to main moodle_backup.xml file
1044 return $moodle_backup;
1045 }
1046
1047}
1048
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 */
1053class backup_activity_grade_items_to_ids extends backup_execution_step {
1054
1055 protected function define_execution() {
1056
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 }
1067}
1068
1069/**
1070 * This step will annotate all the groups belonging to already annotated groupings
1071 */
1072class backup_annotate_groups_from_groupings extends backup_execution_step {
1073
1074 protected function define_execution() {
1075 global $DB;
1076
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 }
1090}
1091
1092/**
1093 * This step will annotate all the scales belonging to already annotated outcomes
1094 */
1095class backup_annotate_scales_from_outcomes extends backup_execution_step {
1096
1097 protected function define_execution() {
1098 global $DB;
1099
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 }
1112}
1113
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 */
1121class backup_annotate_all_user_files extends backup_execution_step {
1122
1123 protected function define_execution() {
1124 global $DB;
1125
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');
1130
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 }
1146}
1147
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 */
1152class backup_activity_grades_structure_step extends backup_structure_step {
1153
1154 protected function define_structure() {
1155
1156 // To know if we are including userinfo
1157 $userinfo = $this->get_setting_value('userinfo');
1158
1159 // Define each element separated
1160
1161 $book = new backup_nested_element('activity_gradebook');
1162
1163 $items = new backup_nested_element('grade_items');
1164
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'));
1173
1174 $grades = new backup_nested_element('grade_grades');
1175
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'));
1182
1183 $letters = new backup_nested_element('grade_letters');
1184
1185 $letter = new backup_nested_element('grade_letter', 'id', array(
1186 'lowerboundary', 'letter'));
1187
1188 // Build the tree
1189
1190 $book->add_child($items);
1191 $items->add_child($item);
1192
1193 $item->add_child($grades);
1194 $grades->add_child($grade);
1195
1196 $book->add_child($letters);
1197 $letters->add_child($letter);
1198
1199 // Define sources
1200
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));
1207
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 }
1212
1213 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1214
1215 // Annotations
1216
1217 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1218 $item->annotate_ids('outcome', 'outcomeid');
1219
1220 $grade->annotate_ids('user', 'userid');
1221 $grade->annotate_ids('user', 'usermodified');
1222
1223 // Return the root element (book)
1224
1225 return $book;
1226 }
1227}