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