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