Updated the HEAD build version to 20100713
[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 */
2de3539b
EL
28
29/**
30 * create the temp dir where backup/restore will happen,
31 * delete old directories and create temp ids table
32 */
77547b46
EL
33class create_and_clean_temp_stuff extends backup_execution_step {
34
35 protected function define_execution() {
36 backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
37 backup_helper::clear_backup_dir($this->get_backupid()); // Empty temp dir, just in case
38 backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
39 backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
40 }
41}
42
2de3539b
EL
43/**
44 * delete the temp dir used by backup/restore (conditionally),
45 * delete old directories and drop tem ids table. Note we delete
39b5371c 46 * the directory but not the corresponding log file that will be
2de3539b
EL
47 * there for, at least, 4 hours - only delete_old_backup_dirs()
48 * deletes log files (for easier access to them)
49 */
50class drop_and_clean_temp_stuff extends backup_execution_step {
51
52 protected function define_execution() {
53 global $CFG;
54 backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
55 backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
56 if (empty($CFG->keeptempdirectoriesonbackup)) { // Conditionally
57 backup_helper::delete_backup_dir($this->get_backupid()); // Empty backup dir
58 }
59 }
60}
61
77547b46
EL
62/**
63 * Create the directory where all the task (activity/block...) information will be stored
64 */
65class create_taskbasepath_directory extends backup_execution_step {
66
67 protected function define_execution() {
68 global $CFG;
69 $basepath = $this->task->get_taskbasepath();
70 if (!check_dir_exists($basepath, true, true)) {
71 throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
72 }
73 }
74}
75
76/**
39b5371c 77 * Abstract structure step, parent of all the activity structure steps. Used to wrap the
060df4c8
EL
78 * activity structure definition within the main <activity ...> tag. Also provides
79 * subplugin support for activities (that must be properly declared)
77547b46
EL
80 */
81abstract class backup_activity_structure_step extends backup_structure_step {
82
4abf04ea
EL
83 /**
84 * Add subplugin structure to any element in the activity backup tree
85 *
86 * @param string $subplugintype type of subplugin as defined in activity db/subplugins.php
87 * @param backup_nested_element $element element in the activity backup tree that
88 * we are going to add subplugin information to
89 * @param bool $multiple to define if multiple subplugins can produce information
90 * for each instance of $element (true) or no (false)
91 */
92 protected function add_subplugin_structure($subplugintype, $element, $multiple) {
060df4c8
EL
93
94 global $CFG;
95
4abf04ea 96 // Check the requested subplugintype is a valid one
060df4c8
EL
97 $subpluginsfile = $CFG->dirroot . '/mod/' . $this->task->get_modulename() . '/db/subplugins.php';
98 if (!file_exists($subpluginsfile)) {
99 throw new backup_step_exception('activity_missing_subplugins_php_file', $this->task->get_modulename());
100 }
101 include($subpluginsfile);
4abf04ea
EL
102 if (!array_key_exists($subplugintype, $subplugins)) {
103 throw new backup_step_exception('incorrect_subplugin_type', $subplugintype);
060df4c8
EL
104 }
105
106 // Arrived here, subplugin is correct, let's create the optigroup
4abf04ea 107 $optigroupname = $subplugintype . '_' . $element->get_name() . '_subplugin';
060df4c8 108 $optigroup = new backup_optigroup($optigroupname, null, $multiple);
f2e34ee5 109 $element->add_child($optigroup); // Add optigroup to stay connected since beginning
060df4c8 110
39b5371c 111 // Get all the optigroup_elements, looking across all the subplugin dirs
060df4c8 112 $elements = array();
4abf04ea 113 $subpluginsdirs = get_plugin_list($subplugintype);
060df4c8 114 foreach ($subpluginsdirs as $name => $subpluginsdir) {
4abf04ea 115 $classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin';
060df4c8
EL
116 $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
117 if (file_exists($backupfile)) {
118 require_once($backupfile);
f2e34ee5
EL
119 $backupsubplugin = new $classname($subplugintype, $name, $optigroup);
120 // Add subplugin returned structure to optigroup
121 $backupsubplugin->define_subplugin_structure($element->get_name());
060df4c8
EL
122 }
123 }
060df4c8
EL
124 }
125
4abf04ea
EL
126 /**
127 * Wraps any activity backup structure within the common 'activity' element
128 * that will include common to all activities information like id, context...
129 */
77547b46
EL
130 protected function prepare_activity_structure($activitystructure) {
131
132 // Create the wrap element
133 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
134
135 // Build the tree
136 $activity->add_child($activitystructure);
137
138 // Set the source
139 $activityarr = array((object)array(
140 'id' => $this->task->get_activityid(),
141 'moduleid' => $this->task->get_moduleid(),
142 'modulename' => $this->task->get_modulename(),
143 'contextid' => $this->task->get_contextid()));
144
145 $activity->set_source_array($activityarr);
146
147 // Return the root element (activity)
148 return $activity;
149 }
150}
151
152/**
39b5371c 153 * Abstract structure step, parent of all the block structure steps. Used to wrap the
77547b46
EL
154 * block structure definition within the main <block ...> tag
155 */
156abstract class backup_block_structure_step extends backup_structure_step {
157
158 protected function prepare_block_structure($blockstructure) {
159
160 // Create the wrap element
161 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
162
163 // Build the tree
164 $block->add_child($blockstructure);
165
166 // Set the source
167 $blockarr = array((object)array(
168 'id' => $this->task->get_blockid(),
169 'blockname' => $this->task->get_blockname(),
170 'contextid' => $this->task->get_contextid()));
171
172 $block->set_source_array($blockarr);
173
174 // Return the root element (block)
175 return $block;
176 }
177}
178
179/**
180 * structure step that will generate the module.xml file for the activity,
39b5371c 181 * accumulating various information about the activity, annotating groupings
77547b46
EL
182 * and completion/avail conf
183 */
184class backup_module_structure_step extends backup_structure_step {
185
186 protected function define_structure() {
187
188 // Define each element separated
189
190 $module = new backup_nested_element('module', array('id', 'version'), array(
191 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
192 'added', 'score', 'indent', 'visible',
193 'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
194 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
195 'availablefrom', 'availableuntil', 'showavailability'));
196
197 $availinfo = new backup_nested_element('availability_info');
198 $availability = new backup_nested_element('availability', array('id'), array(
199 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
200
201 // Define the tree
202 $module->add_child($availinfo);
203 $availinfo->add_child($availability);
204
205 // Set the sources
206
207 $module->set_source_sql('
208 SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
209 FROM {course_modules} cm
210 JOIN {modules} m ON m.id = cm.module
211 JOIN {course_sections} s ON s.id = cm.section
212 WHERE cm.id = ?', array(backup::VAR_MODID));
213
214 $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
215
216 // Define annotations
217 $module->annotate_ids('grouping', 'groupingid');
218
219 // Return the root element ($module)
220 return $module;
221 }
222}
223
224/**
39b5371c 225 * structure step that will generate the section.xml file for the section
77547b46
EL
226 * annotating files
227 */
228class backup_section_structure_step extends backup_structure_step {
229
230 protected function define_structure() {
231
232 // Define each element separated
233
234 $section = new backup_nested_element('section', array('id'), array(
e34a326f 235 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
77547b46
EL
236
237 // Define sources
238
239 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
240
cd00f9b7
EL
241 // Aliases
242 $section->set_source_alias('section', 'number');
243
77547b46 244 // Set annotations
64f93798 245 $section->annotate_files('course', 'section', 'id');
77547b46
EL
246
247 return $section;
248 }
249}
250
251/**
252 * structure step that will generate the course.xml file for the course, including
df997f84 253 * course category reference, tags, modules restriction information
77547b46
EL
254 * and some annotations (files & groupings)
255 */
256class backup_course_structure_step extends backup_structure_step {
257
258 protected function define_structure() {
259 global $DB;
260
261 // Define each element separated
262
263 $course = new backup_nested_element('course', array('id', 'contextid'), array(
df997f84 264 'shortname', 'fullname', 'idnumber',
77547b46 265 'summary', 'summaryformat', 'format', 'showgrades',
df997f84 266 'newsitems', 'startdate',
77547b46
EL
267 'numsections', 'marker', 'maxbytes', 'showreports',
268 'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
df997f84
PS
269 'defaultgroupingid', 'lang', 'theme',
270 'timecreated', 'timemodified',
271 'requested', 'restrictmodules',
272 'enablecompletion'));
77547b46
EL
273
274 $category = new backup_nested_element('category', array('id'), array(
275 'name', 'description'));
276
277 $tags = new backup_nested_element('tags');
278
279 $tag = new backup_nested_element('tag', array('id'), array(
280 'name', 'rawname'));
281
282 $allowedmodules = new backup_nested_element('allowed_modules');
283
284 $module = new backup_nested_element('module', array('modulename'));
285
286 // Build the tree
287
288 $course->add_child($category);
289
290 $course->add_child($tags);
291 $tags->add_child($tag);
292
293 $course->add_child($allowedmodules);
294 $allowedmodules->add_child($module);
295
296 // Set the sources
297
298 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
299 $courserec->contextid = $this->task->get_contextid();
300
301 $course->set_source_array(array($courserec));
302
303 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
304
305 $category->set_source_array(array($categoryrec));
306
307 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
308 FROM {tag} t
309 JOIN {tag_instance} ti ON ti.tagid = t.id
310 WHERE ti.itemtype = ?
311 AND ti.itemid = ?', array(
c0bd6249 312 backup_helper::is_sqlparam('course'),
77547b46
EL
313 backup::VAR_PARENTID));
314
315 $module->set_source_sql('SELECT m.name AS modulename
316 FROM {modules} m
317 JOIN {course_allowed_modules} cam ON m.id = cam.module
318 WHERE course = ?', array(backup::VAR_COURSEID));
319
320 // Some annotations
321
77547b46
EL
322 $course->annotate_ids('grouping', 'defaultgroupingid');
323
64f93798
PS
324 $course->annotate_files('course', 'summary', null);
325 $course->annotate_files('course', 'legacy', null);
77547b46
EL
326
327 // Return root element ($course)
328
329 return $course;
330 }
331}
332
cb34c4cd
PS
333/**
334 * structure step that will generate the enrolments.xml file for the given course
335 */
336class backup_enrolments_structure_step extends backup_structure_step {
337
338 protected function define_structure() {
339
340 // To know if we are including users
341 $users = $this->get_setting_value('users');
342
343 // Define each element separated
344
345 $enrolments = new backup_nested_element('enrolments');
346
347 $enrols = new backup_nested_element('enrols');
348
349 $enrol = new backup_nested_element('enrol', array('id'), array(
101bd9c9 350 'enrol', 'status', 'sortorder', 'name', 'enrolperiod', 'enrolstartdate',
cb34c4cd
PS
351 'enrolenddate', 'expirynotify', 'expirytreshold', 'notifyall',
352 'password', 'cost', 'currency', 'roleid', 'customint1', 'customint2', 'customint3',
353 'customint4', 'customchar1', 'customchar2', 'customdec1', 'customdec2',
354 'customtext1', 'customtext2', 'timecreated', 'timemodified'));
355
356 $userenrolments = new backup_nested_element('user_enrolments');
357
358 $enrolment = new backup_nested_element('enrolment', array('id'), array(
101bd9c9 359 'status', 'userid', 'timestart', 'timeend', 'modifierid',
cb34c4cd
PS
360 'timemodified'));
361
362 // Build the tree
363 $enrolments->add_child($enrols);
364 $enrols->add_child($enrol);
365 $enrol->add_child($userenrolments);
366 $userenrolments->add_child($enrolment);
367
368 // Define sources
369
370 $enrol->set_source_table('enrol', array('courseid' => backup::VAR_COURSEID));
371
101bd9c9 372 // User enrolments only added only if users included
cb34c4cd
PS
373 if ($users) {
374 $enrolment->set_source_table('user_enrolments', array('enrolid' => backup::VAR_PARENTID));
02eca29c 375 $enrolment->annotate_ids('user', 'userid');
cb34c4cd
PS
376 }
377
902944a8
PS
378 $enrol->annotate_ids('role', 'roleid');
379
cb34c4cd
PS
380 //TODO: let plugins annotate custom fields too and add more children
381
382 return $enrolments;
383 }
384}
385
77547b46
EL
386/**
387 * structure step that will generate the roles.xml file for the given context, observing
388 * the role_assignments setting to know if that part needs to be included
389 */
390class backup_roles_structure_step extends backup_structure_step {
391
392 protected function define_structure() {
393
394 // To know if we are including role assignments
395 $roleassignments = $this->get_setting_value('role_assignments');
396
397 // Define each element separated
398
399 $roles = new backup_nested_element('roles');
400
401 $overrides = new backup_nested_element('role_overrides');
402
403 $override = new backup_nested_element('override', array('id'), array(
404 'roleid', 'capability', 'permission', 'timemodified',
405 'modifierid'));
406
407 $assignments = new backup_nested_element('role_assignments');
408
409 $assignment = new backup_nested_element('assignment', array('id'), array(
cb34c4cd 410 'roleid', 'userid', 'timemodified', 'modifierid', 'component', 'itemid',
77547b46
EL
411 'sortorder'));
412
413 // Build the tree
414 $roles->add_child($overrides);
415 $roles->add_child($assignments);
416
417 $overrides->add_child($override);
418 $assignments->add_child($assignment);
419
420 // Define sources
421
422 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
423
424 // Assignments only added if specified
425 if ($roleassignments) {
426 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
427 }
428
429 // Define id annotations
430 $override->annotate_ids('role', 'roleid');
431
432 $assignment->annotate_ids('role', 'roleid');
433
434 $assignment->annotate_ids('user', 'userid');
435
cb34c4cd
PS
436 //TODO: how do we annotate the itemid? the meaning depends on the content of component table (skodak)
437
77547b46
EL
438 return $roles;
439 }
440}
441
442/**
443 * structure step that will generate the roles.xml containing the
444 * list of roles used along the whole backup process. Just raw
445 * list of used roles from role table
446 */
447class backup_final_roles_structure_step extends backup_structure_step {
448
449 protected function define_structure() {
450
451 // Define elements
452
453 $rolesdef = new backup_nested_element('roles_definition');
454
455 $role = new backup_nested_element('role', array('id'), array(
456 'name', 'shortname', 'nameincourse', 'description',
457 'sortorder', 'archetype'));
458
459 // Build the tree
460
461 $rolesdef->add_child($role);
462
463 // Define sources
464
465 $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
466 FROM {role} r
467 JOIN {backup_ids_temp} bi ON r.id = bi.itemid
468 LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
469 WHERE bi.backupid = ?
470 AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
471
472 // Return main element (rolesdef)
473 return $rolesdef;
474 }
475}
476
477/**
478 * structure step that will generate the scales.xml containing the
479 * list of scales used along the whole backup process.
480 */
481class backup_final_scales_structure_step extends backup_structure_step {
482
483 protected function define_structure() {
484
485 // Define elements
486
487 $scalesdef = new backup_nested_element('scales_definition');
488
489 $scale = new backup_nested_element('scale', array('id'), array(
490 'courseid', 'userid', 'name', 'scale',
491 'description', 'descriptionformat', 'timemodified'));
492
493 // Build the tree
494
495 $scalesdef->add_child($scale);
496
497 // Define sources
498
499 $scale->set_source_sql("SELECT s.*
500 FROM {scale} s
501 JOIN {backup_ids_temp} bi ON s.id = bi.itemid
502 WHERE bi.backupid = ?
503 AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
504
505 // Return main element (scalesdef)
506 return $scalesdef;
507 }
508}
509
510/**
511 * structure step that will generate the outcomes.xml containing the
512 * list of outcomes used along the whole backup process.
513 */
514class backup_final_outcomes_structure_step extends backup_structure_step {
515
516 protected function define_structure() {
517
518 // Define elements
519
520 $outcomesdef = new backup_nested_element('outcomes_definition');
521
522 $outcome = new backup_nested_element('outcome', array('id'), array(
523 'courseid', 'userid', 'shortname', 'fullname',
524 'scaleid', 'description', 'descriptionformat', 'timecreated',
525 'timemodified','usermodified'));
526
527 // Build the tree
528
529 $outcomesdef->add_child($outcome);
530
531 // Define sources
532
533 $outcome->set_source_sql("SELECT o.*
534 FROM {grade_outcomes} o
535 JOIN {backup_ids_temp} bi ON o.id = bi.itemid
536 WHERE bi.backupid = ?
537 AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
538
539 // Return main element (outcomesdef)
540 return $outcomesdef;
541 }
542}
543
544/**
545 * structure step in charge of constructing the filters.xml file for all the filters found
546 * in activity
547 */
548class backup_filters_structure_step extends backup_structure_step {
549
550 protected function define_structure() {
551
552 // Define each element separated
553
554 $filters = new backup_nested_element('filters');
555
556 $actives = new backup_nested_element('filter_actives');
557
558 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
559
560 $configs = new backup_nested_element('filter_configs');
561
562 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
563
564 // Build the tree
565
566 $filters->add_child($actives);
567 $filters->add_child($configs);
568
569 $actives->add_child($active);
570 $configs->add_child($config);
571
572 // Define sources
573
574 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
575
576 $active->set_source_array($activearr);
577 $config->set_source_array($configarr);
578
579 // Return the root element (filters)
580 return $filters;
581 }
582}
583
584/**
585 * structure step in charge of constructing the comments.xml file for all the comments found
586 * in a given context
587 */
588class backup_comments_structure_step extends backup_structure_step {
589
590 protected function define_structure() {
591
592 // Define each element separated
593
594 $comments = new backup_nested_element('comments');
595
596 $comment = new backup_nested_element('comment', array('id'), array(
597 'commentarea', 'itemid', 'content', 'format',
598 'userid', 'timecreated'));
599
600 // Build the tree
601
602 $comments->add_child($comment);
603
604 // Define sources
605
606 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
607
608 // Define id annotations
609
610 $comment->annotate_ids('user', 'userid');
611
612 // Return the root element (comments)
613 return $comments;
614 }
615}
616
617/**
618 * structure step in charge if constructing the completion.xml file for all the users completion
619 * information in a given activity
620 */
621class backup_userscompletion_structure_step extends backup_structure_step {
622
623 protected function define_structure() {
624
625 // Define each element separated
626
627 $completions = new backup_nested_element('completions');
628
629 $completion = new backup_nested_element('completion', array('id'), array(
630 'userid', 'completionstate', 'viewed', 'timemodified'));
631
632 // Build the tree
633
634 $completions->add_child($completion);
635
636 // Define sources
637
638 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
639
640 // Define id annotations
641
642 $completion->annotate_ids('user', 'userid');
643
644 // Return the root element (completions)
645 return $completions;
646 }
647}
648
649/**
650 * structure step in charge of constructing the main groups.xml file for all the groups and
651 * groupings information already annotated
652 */
653class backup_groups_structure_step extends backup_structure_step {
654
655 protected function define_structure() {
656
657 // To know if we are including users
658 $users = $this->get_setting_value('users');
659
660 // Define each element separated
661
662 $groups = new backup_nested_element('groups');
663
664 $group = new backup_nested_element('group', array('id'), array(
665 'name', 'description', 'descriptionformat', 'enrolmentkey',
666 'picture', 'hidepicture', 'timecreated', 'timemodified'));
667
668 $members = new backup_nested_element('group_members');
669
670 $member = new backup_nested_element('group_member', array('id'), array(
671 'userid', 'timeadded'));
672
673 $groupings = new backup_nested_element('groupings');
674
675 $grouping = new backup_nested_element('grouping', 'id', array(
676 'name', 'description', 'descriptionformat', 'configdata',
677 'timecreated', 'timemodified'));
678
679 $groupinggroups = new backup_nested_element('grouping_groups');
680
681 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
682 'groupid', 'timeadded'));
683
684 // Build the tree
685
686 $groups->add_child($group);
687 $groups->add_child($groupings);
688
689 $group->add_child($members);
690 $members->add_child($member);
691
692 $groupings->add_child($grouping);
693 $grouping->add_child($groupinggroups);
694 $groupinggroups->add_child($groupinggroup);
695
696 // Define sources
697
698 $group->set_source_sql("
699 SELECT g.*
700 FROM {groups} g
701 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
702 WHERE bi.backupid = ?
703 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
704
705 // This only happens if we are including users
706 if ($users) {
707 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
708 }
709
710 $grouping->set_source_sql("
711 SELECT g.*
712 FROM {groupings} g
713 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
714 WHERE bi.backupid = ?
715 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
716
717 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
718
719 // Define id annotations (as final)
720
721 $member->annotate_ids('userfinal', 'userid');
722
723 // Define file annotations
724
64f93798 725 $group->annotate_files('group', 'description', 'id');
78d47b30 726 $group->annotate_files('group', 'icon', 'id');
77547b46
EL
727
728 // Return the root element (groups)
729 return $groups;
730 }
731}
732
733/**
734 * structure step in charge of constructing the main users.xml file for all the users already
735 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
736 * overrides.
737 */
738class backup_users_structure_step extends backup_structure_step {
739
740 protected function define_structure() {
741 global $CFG;
742
743 // To know if we are anonymizing users
744 $anonymize = $this->get_setting_value('anonymize');
745 // To know if we are including role assignments
746 $roleassignments = $this->get_setting_value('role_assignments');
747
748 // Define each element separated
749
750 $users = new backup_nested_element('users');
751
752 // Create the array of user fields by hand, as far as we have various bits to control
753 // anonymize option, password backup, mnethostid...
754
755 // First, the fields not needing anonymization nor special handling
756 $normalfields = array(
757 'confirmed', 'policyagreed', 'deleted',
758 'lang', 'theme', 'timezone', 'firstaccess',
759 'lastaccess', 'lastlogin', 'currentlogin', 'secret',
760 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
761 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
762 'timemodified', 'trustbitmask', 'screenreader');
763
764 // Then, the fields potentially needing anonymization
765 $anonfields = array(
766 'username', 'idnumber', 'firstname', 'lastname',
767 'email', 'emailstop', 'lastip', 'picture',
768 'url', 'description', 'description_format', 'imagealt', 'auth');
769
770 // Add anonymized fields to $userfields with custom final element
771 foreach ($anonfields as $field) {
772 if ($anonymize) {
773 $userfields[] = new anonymizer_final_element($field);
774 } else {
775 $userfields[] = $field; // No anonymization, normally added
776 }
777 }
778
779 // mnethosturl requires special handling (custom final element)
780 $userfields[] = new mnethosturl_final_element('mnethosturl');
781
782 // password added conditionally
783 if (!empty($CFG->includeuserpasswordsinbackup)) {
784 $userfields[] = 'password';
785 }
786
787 // Merge all the fields
788 $userfields = array_merge($userfields, $normalfields);
789
790 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
791
792 $customfields = new backup_nested_element('custom_fields');
793
794 $customfield = new backup_nested_element('custom_field', array('id'), array(
795 'field_name', 'field_type', 'field_data'));
796
797 $tags = new backup_nested_element('tags');
798
799 $tag = new backup_nested_element('tag', array('id'), array(
800 'name', 'rawname'));
801
802 $preferences = new backup_nested_element('preferences');
803
804 $preference = new backup_nested_element('preference', array('id'), array(
805 'name', 'value'));
806
807 $roles = new backup_nested_element('roles');
808
809 $overrides = new backup_nested_element('role_overrides');
810
811 $override = new backup_nested_element('override', array('id'), array(
812 'roleid', 'capability', 'permission', 'timemodified',
813 'modifierid'));
814
815 $assignments = new backup_nested_element('role_assignments');
816
817 $assignment = new backup_nested_element('assignment', array('id'), array(
df997f84 818 'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
77547b46
EL
819 'sortorder'));
820
821 // Build the tree
822
823 $users->add_child($user);
824
825 $user->add_child($customfields);
826 $customfields->add_child($customfield);
827
828 $user->add_child($tags);
829 $tags->add_child($tag);
830
831 $user->add_child($preferences);
832 $preferences->add_child($preference);
833
834 $user->add_child($roles);
835
836 $roles->add_child($overrides);
837 $roles->add_child($assignments);
838
839 $overrides->add_child($override);
840 $assignments->add_child($assignment);
841
842 // Define sources
843
844 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
845 FROM {user} u
846 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
847 JOIN {context} c ON c.instanceid = u.id
848 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
849 WHERE bi.backupid = ?
850 AND bi.itemname = ?
851 AND c.contextlevel = ?', array(
c0bd6249
EL
852 backup_helper::is_sqlparam($this->get_backupid()),
853 backup_helper::is_sqlparam('userfinal'),
854 backup_helper::is_sqlparam(CONTEXT_USER)));
77547b46
EL
855
856 // All the rest on information is only added if we arent
857 // in an anonymized backup
858 if (!$anonymize) {
859 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
860 FROM {user_info_field} f
861 JOIN {user_info_data} d ON d.fieldid = f.id
862 WHERE d.userid = ?', array(backup::VAR_PARENTID));
863
864 $customfield->set_source_alias('shortname', 'field_name');
865 $customfield->set_source_alias('datatype', 'field_type');
866 $customfield->set_source_alias('data', 'field_data');
867
868 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
869 FROM {tag} t
870 JOIN {tag_instance} ti ON ti.tagid = t.id
871 WHERE ti.itemtype = ?
872 AND ti.itemid = ?', array(
c0bd6249 873 backup_helper::is_sqlparam('user'),
77547b46
EL
874 backup::VAR_PARENTID));
875
876 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
877
878 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
879
880 // Assignments only added if specified
881 if ($roleassignments) {
882 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
883 }
884
885 // Define id annotations (as final)
886 $override->annotate_ids('rolefinal', 'roleid');
887 }
888
889 // Return root element (users)
890 return $users;
891 }
892}
893
894/**
895 * structure step in charge of constructing the block.xml file for one
39b5371c 896 * given block (instance and positions). If the block has custom DB structure
77547b46
EL
897 * that will go to a separate file (different step defined in block class)
898 */
899class backup_block_instance_structure_step extends backup_structure_step {
900
901 protected function define_structure() {
902 global $DB;
903
904 // Define each element separated
905
5f8354eb 906 $block = new backup_nested_element('block', array('id', 'contextid', 'version'), array(
61243f3a
EL
907 'blockname', 'parentcontextid', 'showinsubcontexts', 'pagetypepattern',
908 'subpagepattern', 'defaultregion', 'defaultweight', 'configdata'));
77547b46
EL
909
910 $positions = new backup_nested_element('block_positions', null, array(
911 'contextid', 'pagetype', 'subpage', 'visible',
912 'region', 'weight'));
913
914 // Build the tree
915
916 $block->add_child($positions);
917
918 // Transform configdata information if needed (process links and friends)
919 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
920 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
921 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
922 foreach ($configdata as $attribute => $value) {
923 if (in_array($attribute, $attrstotransform)) {
924 $configdata[$attribute] = $this->contenttransformer->process($value);
925 }
926 }
927 $blockrec->configdata = base64_encode(serialize((object)$configdata));
928 }
5f8354eb 929 $blockrec->contextid = $this->task->get_contextid();
77547b46
EL
930 // Get the version of the block
931 $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
932
933 // Define sources
934
935 $block->set_source_array(array($blockrec));
936
937 $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
938
939 // Return the root element (block)
940 return $block;
941 }
942}
943
944/**
945 * structure step in charge of constructing the logs.xml file for all the log records found
946 * in activity
947 */
948class backup_activity_logs_structure_step extends backup_structure_step {
949
950 protected function define_structure() {
951
952 // Define each element separated
953
954 $logs = new backup_nested_element('logs');
955
956 $log = new backup_nested_element('log', array('id'), array(
957 'time', 'userid', 'ip', 'module',
958 'action', 'url', 'info'));
959
960 // Build the tree
961
962 $logs->add_child($log);
963
964 // Define sources
965
966 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
967
968 // Annotations
969 // NOTE: We don't annotate users from logs as far as they MUST be
970 // always annotated by the activity.
971
972 // Return the root element (logs)
973
974 return $logs;
975 }
976}
977
978/**
979 * structure in charge of constructing the inforef.xml file for all the items we want
980 * to have referenced there (users, roles, files...)
981 */
982class backup_inforef_structure_step extends backup_structure_step {
983
984 protected function define_structure() {
985
986 // Items we want to include in the inforef file. NOTE: Important to keep this
987 // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
988 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
989
990 // Build the tree
991
992 $inforef = new backup_nested_element('inforef');
993
994 // For each item, conditionally, if there are already records, build element
995 foreach ($items as $itemname) {
996 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
997 $elementroot = new backup_nested_element($itemname . 'ref');
998 $element = new backup_nested_element($itemname, array('id'));
999 $inforef->add_child($elementroot);
1000 $elementroot->add_child($element);
1001 $element->set_source_sql("
1002 SELECT itemid AS id
1003 FROM {backup_ids_temp}
1004 WHERE backupid = ?
1005 AND itemname = ?",
c0bd6249 1006 array(backup::VAR_BACKUPID, backup_helper::is_sqlparam($itemname)));
77547b46
EL
1007 }
1008 }
1009
1010 // We don't annotate anything there, but rely in the next step
1011 // (move_inforef_annotations_to_final) that will change all the
1012 // already saved 'inforref' entries to their 'final' annotations.
1013 return $inforef;
1014 }
1015}
1016
1017/**
1018 * This step will get all the annotations already processed to inforef.xml file and
1019 * transform them into 'final' annotations.
1020 */
1021class move_inforef_annotations_to_final extends backup_execution_step {
1022
1023 protected function define_execution() {
1024
1025 // Items we want to include in the inforef file. NOTE: Important to keep this
1026 // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
1027 $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
1028 foreach ($items as $itemname) {
1029 // Delegate to dbops
1030 backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
1031 }
1032 }
1033}
1034
1035/**
1036 * structure in charge of constructing the files.xml file with all the
1037 * annotated (final) files along the process. At, the same time, and
1038 * using one specialised nested_element, will copy them form moodle storage
1039 * to backup storage
1040 */
1041class backup_final_files_structure_step extends backup_structure_step {
1042
1043 protected function define_structure() {
1044
1045 // Define elements
1046
1047 $files = new backup_nested_element('files');
1048
1049 $file = new file_nested_element('file', array('id'), array(
64f93798 1050 'contenthash', 'contextid', 'component', 'filearea', 'itemid',
77547b46
EL
1051 'filepath', 'filename', 'userid', 'filesize',
1052 'mimetype', 'status', 'timecreated', 'timemodified',
1fd3ea43 1053 'source', 'author', 'license', 'sortorder'));
77547b46
EL
1054
1055 // Build the tree
1056
1057 $files->add_child($file);
1058
1059 // Define sources
1060
1061 $file->set_source_sql("SELECT f.*
1062 FROM {files} f
1063 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
1064 WHERE bi.backupid = ?
1065 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
1066
1067 return $files;
1068 }
1069}
1070
1071/**
1072 * Structure step in charge of creating the main moodle_backup.xml file
1073 * where all the information related to the backup, settings, license and
1074 * other information needed on restore is added*/
1075class backup_main_structure_step extends backup_structure_step {
1076
1077 protected function define_structure() {
1078
1079 global $CFG;
1080
1081 $info = array();
1082
1083 $info['name'] = $this->get_setting_value('filename');
1084 $info['moodle_version'] = $CFG->version;
1085 $info['moodle_release'] = $CFG->release;
1086 $info['backup_version'] = $CFG->backup_version;
1087 $info['backup_release'] = $CFG->backup_release;
1088 $info['backup_date'] = time();
1089 $info['backup_uniqueid']= $this->get_backupid();
1090 $info['original_wwwroot']=$CFG->wwwroot;
1091 $info['original_site_identifier'] = get_site_identifier();
1092 $info['original_course_id'] = $this->get_courseid();
1093
1094 // Get more information from controller
1095 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
1096
1097 // Define elements
1098
1099 $moodle_backup = new backup_nested_element('moodle_backup');
1100
1101 $information = new backup_nested_element('information', null, array(
1102 'name', 'moodle_version', 'moodle_release', 'backup_version',
1103 'backup_release', 'backup_date', 'original_wwwroot',
1104 'original_site_identifier', 'original_course_id'));
1105
1106 $details = new backup_nested_element('details');
1107
1108 $detail = new backup_nested_element('detail', array('backup_id'), array(
1109 'type', 'format', 'interactive', 'mode',
1110 'execution', 'executiontime'));
1111
1112 $contents = new backup_nested_element('contents');
1113
1114 $activities = new backup_nested_element('activities');
1115
1116 $activity = new backup_nested_element('activity', null, array(
1117 'moduleid', 'sectionid', 'modulename', 'title',
1118 'directory'));
1119
1120 $sections = new backup_nested_element('sections');
1121
1122 $section = new backup_nested_element('section', null, array(
1123 'sectionid', 'title', 'directory'));
1124
1125 $course = new backup_nested_element('course', null, array(
1126 'courseid', 'title', 'directory'));
1127
1128 $settings = new backup_nested_element('settings');
1129
1130 $setting = new backup_nested_element('setting', null, array(
d12fd69b 1131 'level', 'section', 'activity', 'name', 'value'));
77547b46
EL
1132
1133 // Build the tree
1134
1135 $moodle_backup->add_child($information);
1136
1137 $information->add_child($details);
1138 $details->add_child($detail);
1139
1140 $information->add_child($contents);
1141 if (!empty($cinfo['activities'])) {
1142 $contents->add_child($activities);
1143 $activities->add_child($activity);
1144 }
1145 if (!empty($cinfo['sections'])) {
1146 $contents->add_child($sections);
1147 $sections->add_child($section);
1148 }
1149 if (!empty($cinfo['course'])) {
1150 $contents->add_child($course);
1151 }
1152
1153 $information->add_child($settings);
1154 $settings->add_child($setting);
1155
1156
1157 // Set the sources
1158
1159 $information->set_source_array(array((object)$info));
1160
1161 $detail->set_source_array($dinfo);
1162
1163 $activity->set_source_array($cinfo['activities']);
1164
1165 $section->set_source_array($cinfo['sections']);
1166
1167 $course->set_source_array($cinfo['course']);
1168
1169 $setting->set_source_array($sinfo);
1170
1171 // Prepare some information to be sent to main moodle_backup.xml file
1172 return $moodle_backup;
1173 }
1174
1175}
1176
ce937f99
EL
1177/**
1178 * Execution step that will generate the final zip file with all the contents
1179 */
1180class backup_zip_contents extends backup_execution_step {
1181
1182 protected function define_execution() {
1183
1184 // Get basepath
1185 $basepath = $this->get_basepath();
1186
1187 // Get the list of files in directory
1188 $filestemp = get_directory_list($basepath, '', false, true, true);
1189 $files = array();
1190 foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1191 $files[$file] = $basepath . '/' . $file;
1192 }
1193
1194 // Add the log file if exists
1195 $logfilepath = $basepath . '.log';
1196 if (file_exists($logfilepath)) {
1197 $files['moodle_backup.log'] = $logfilepath;
1198 }
1199
9eeaea5f
EL
1200 // Calculate the zip fullpath (in OS temp area it's always backup.zip)
1201 $zipfile = $basepath . '/backup.zip';
ce937f99
EL
1202
1203 // Get the zip packer
1204 $zippacker = get_file_packer('application/zip');
1205
1206 // Zip files
1207 $zippacker->archive_to_pathname($files, $zipfile);
1208 }
1209}
1210
1211/**
1212 * This step will send the generated backup file to its final destination
1213 */
1214class backup_store_backup_file extends backup_execution_step {
1215
1216 protected function define_execution() {
1217
1218 // Get basepath
1219 $basepath = $this->get_basepath();
1220
9eeaea5f
EL
1221 // Calculate the zip fullpath (in OS temp area it's always backup.zip)
1222 $zipfile = $basepath . '/backup.zip';
ce937f99
EL
1223
1224 // Perform storage and return it (TODO: shouldn't be array but proper result object)
1225 return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
1226 }
1227}
1228
1229
77547b46
EL
1230/**
1231 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1232 * and put them to the backup_ids tables, to be used later as base to backup them
1233 */
1234class backup_activity_grade_items_to_ids extends backup_execution_step {
1235
1236 protected function define_execution() {
1237
1238 // Fetch all activity grade items
1239 if ($items = grade_item::fetch_all(array(
1240 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1241 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1242 // Annotate them in backup_ids
1243 foreach ($items as $item) {
1244 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1245 }
1246 }
1247 }
1248}
1249
1250/**
1251 * This step will annotate all the groups belonging to already annotated groupings
1252 */
1253class backup_annotate_groups_from_groupings extends backup_execution_step {
1254
1255 protected function define_execution() {
1256 global $DB;
1257
1258 // Fetch all the annotated groupings
1259 if ($groupings = $DB->get_records('backup_ids_temp', array(
1260 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1261 foreach ($groupings as $grouping) {
1262 if ($groups = $DB->get_records('groupings_groups', array(
1263 'groupingid' => $grouping->itemid))) {
1264 foreach ($groups as $group) {
1265 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1266 }
1267 }
1268 }
1269 }
1270 }
1271}
1272
1273/**
1274 * This step will annotate all the scales belonging to already annotated outcomes
1275 */
1276class backup_annotate_scales_from_outcomes extends backup_execution_step {
1277
1278 protected function define_execution() {
1279 global $DB;
1280
1281 // Fetch all the annotated outcomes
1282 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1283 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1284 foreach ($outcomes as $outcome) {
1285 if ($scale = $DB->get_record('grade_outcomes', array(
1286 'id' => $outcome->itemid))) {
1287 // Annotate as scalefinal because it's > 0
1288 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1289 }
1290 }
1291 }
1292 }
1293}
1294
1295/**
1296 * This step will generate all the user annotations for the already
39b5371c 1297 * annotated (final) users. Need to do this here because each user
77547b46
EL
1298 * has its own context and structure tasks only are able to handle
1299 * one context. Also, this step will guarantee that every user has
1300 * its context created (req for other steps)
1301 */
1302class backup_annotate_all_user_files extends backup_execution_step {
1303
1304 protected function define_execution() {
1305 global $DB;
1306
1307 // List of fileareas we are going to annotate
78d47b30 1308 $fileareas = array('private', 'profile', 'icon');
77547b46
EL
1309
1310 // Fetch all annotated (final) users
1311 $rs = $DB->get_recordset('backup_ids_temp', array(
1312 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1313 foreach ($rs as $record) {
1314 $userid = $record->itemid;
1315 $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1316 // Proceed with every user filearea
1317 foreach ($fileareas as $filearea) {
78d47b30 1318 // We don't need to specify itemid ($userid - 5th param) as far as by
77547b46 1319 // context we can get all the associated files. See MDL-22092
64f93798 1320 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, 'user', $filearea, null);
77547b46
EL
1321 }
1322 }
1323 $rs->close();
1324 }
1325}
1326
1327/**
1328 * structure step in charge of constructing the grades.xml file for all the grade items
1329 * and letters related to one activity
1330 */
1331class backup_activity_grades_structure_step extends backup_structure_step {
1332
1333 protected function define_structure() {
1334
1335 // To know if we are including userinfo
1336 $userinfo = $this->get_setting_value('userinfo');
1337
1338 // Define each element separated
1339
1340 $book = new backup_nested_element('activity_gradebook');
1341
1342 $items = new backup_nested_element('grade_items');
1343
1344 $item = new backup_nested_element('grade_item', array('id'), array(
1345 'categoryid', 'itemname', 'itemtype', 'itemmodule',
1346 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1347 'calculation', 'gradetype', 'grademax', 'grademin',
1348 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1349 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1350 'decimals', 'hidden', 'locked', 'locktime',
1351 'needsupdate', 'timecreated', 'timemodified'));
1352
1353 $grades = new backup_nested_element('grade_grades');
1354
1355 $grade = new backup_nested_element('grade_grade', array('id'), array(
1356 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1357 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1358 'locked', 'locktime', 'exported', 'overridden',
1359 'excluded', 'feedback', 'feedbackformat', 'information',
1360 'informationformat', 'timecreated', 'timemodified'));
1361
1362 $letters = new backup_nested_element('grade_letters');
1363
1364 $letter = new backup_nested_element('grade_letter', 'id', array(
1365 'lowerboundary', 'letter'));
1366
1367 // Build the tree
1368
1369 $book->add_child($items);
1370 $items->add_child($item);
1371
1372 $item->add_child($grades);
1373 $grades->add_child($grade);
1374
1375 $book->add_child($letters);
1376 $letters->add_child($letter);
1377
1378 // Define sources
1379
1380 $item->set_source_sql("
1381 SELECT gi.*
1382 FROM {grade_items} gi
1383 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1384 WHERE bi.backupid = ?
1385 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
1386
1387 // This only happens if we are including user info
1388 if ($userinfo) {
1389 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1390 }
1391
1392 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1393
1394 // Annotations
1395
1396 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1397 $item->annotate_ids('outcome', 'outcomeid');
1398
1399 $grade->annotate_ids('user', 'userid');
1400 $grade->annotate_ids('user', 'usermodified');
1401
1402 // Return the root element (book)
1403
1404 return $book;
1405 }
1406}