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