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