Updated the HEAD build version to 20100921
[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',
58f8ca39
EL
265 'newsitems', 'startdate', 'numsections',
266 'marker', 'maxbytes', 'legacyfiles', 'showreports',
77547b46 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');
0067d939 667 $grade_category = new backup_nested_element('grade_category', array('id'), array(
9a20df96
AD
668 //'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(
e101180d 675 'lowerboundary', 'letter'));
315f6d8e 676
b8040c83
AD
677 $grade_settings = new backup_nested_element('grade_settings');
678 $grade_setting = new backup_nested_element('grade_setting', 'id', array(
679 'name', 'value'));
680
315f6d8e
AD
681
682 // Build the tree
0067d939
AD
683 $gradebook->add_child($grade_categories);
684 $grade_categories->add_child($grade_category);
315f6d8e
AD
685
686 $gradebook->add_child($grade_items);
687 $grade_items->add_child($grade_item);
688 $grade_item->add_child($grade_grades);
689 $grade_grades->add_child($grade_grade);
690
315f6d8e
AD
691 $gradebook->add_child($letters);
692 $letters->add_child($letter);
693
b8040c83
AD
694 $gradebook->add_child($grade_settings);
695 $grade_settings->add_child($grade_setting);
696
315f6d8e
AD
697 // Define sources
698
be739b71 699 //Include manual, category and the course grade item
58328ce8 700 $grade_items_sql ="SELECT * FROM {grade_items}
be739b71
AD
701 WHERE courseid = :courseid
702 AND (itemtype='manual' OR itemtype='course' OR itemtype='category')";
3f92c2fc 703 $grade_items_params = array('courseid'=>backup::VAR_COURSEID);
be739b71 704 $grade_item->set_source_sql($grade_items_sql, $grade_items_params);
315f6d8e
AD
705
706 if ($userinfo) {
707 $grade_grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
708 }
709
710 $grade_category_sql = "SELECT gc.*, gi.sortorder
711 FROM {grade_categories} gc
712 JOIN {grade_items} gi ON (gi.iteminstance = gc.id)
713 WHERE gc.courseid = :courseid
714 AND (gi.itemtype='course' OR gi.itemtype='category')
715 ORDER BY gc.parent ASC";//need parent categories before their children
716 $grade_category_params = array('courseid'=>backup::VAR_COURSEID);
717 $grade_category->set_source_sql($grade_category_sql, $grade_category_params);
718
719 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
720
b8040c83
AD
721 $grade_setting->set_source_table('grade_settings', array('courseid' => backup::VAR_COURSEID));
722
76cfb124 723 // Annotations (both as final as far as they are going to be exported in next steps)
315f6d8e 724 $grade_item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
76cfb124 725 $grade_item->annotate_ids('outcomefinal', 'outcomeid');
315f6d8e 726
9a20df96
AD
727 //just in case there are any users not already annotated by the activities
728 $grade_grade->annotate_ids('userfinal', 'userid');
729
315f6d8e
AD
730 // Return the root element
731 return $gradebook;
732 }
733}
734
77547b46
EL
735/**
736 * structure step in charge if constructing the completion.xml file for all the users completion
737 * information in a given activity
738 */
739class backup_userscompletion_structure_step extends backup_structure_step {
740
741 protected function define_structure() {
742
743 // Define each element separated
744
745 $completions = new backup_nested_element('completions');
746
747 $completion = new backup_nested_element('completion', array('id'), array(
748 'userid', 'completionstate', 'viewed', 'timemodified'));
749
750 // Build the tree
751
752 $completions->add_child($completion);
753
754 // Define sources
755
756 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
757
758 // Define id annotations
759
760 $completion->annotate_ids('user', 'userid');
761
762 // Return the root element (completions)
763 return $completions;
764 }
765}
766
767/**
768 * structure step in charge of constructing the main groups.xml file for all the groups and
769 * groupings information already annotated
770 */
771class backup_groups_structure_step extends backup_structure_step {
772
773 protected function define_structure() {
774
775 // To know if we are including users
776 $users = $this->get_setting_value('users');
777
778 // Define each element separated
779
780 $groups = new backup_nested_element('groups');
781
782 $group = new backup_nested_element('group', array('id'), array(
783 'name', 'description', 'descriptionformat', 'enrolmentkey',
784 'picture', 'hidepicture', 'timecreated', 'timemodified'));
785
786 $members = new backup_nested_element('group_members');
787
788 $member = new backup_nested_element('group_member', array('id'), array(
789 'userid', 'timeadded'));
790
791 $groupings = new backup_nested_element('groupings');
792
793 $grouping = new backup_nested_element('grouping', 'id', array(
794 'name', 'description', 'descriptionformat', 'configdata',
795 'timecreated', 'timemodified'));
796
797 $groupinggroups = new backup_nested_element('grouping_groups');
798
799 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
800 'groupid', 'timeadded'));
801
802 // Build the tree
803
804 $groups->add_child($group);
805 $groups->add_child($groupings);
806
807 $group->add_child($members);
808 $members->add_child($member);
809
810 $groupings->add_child($grouping);
811 $grouping->add_child($groupinggroups);
812 $groupinggroups->add_child($groupinggroup);
813
814 // Define sources
815
816 $group->set_source_sql("
817 SELECT g.*
818 FROM {groups} g
819 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
820 WHERE bi.backupid = ?
821 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
822
823 // This only happens if we are including users
824 if ($users) {
825 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
826 }
827
828 $grouping->set_source_sql("
829 SELECT g.*
830 FROM {groupings} g
831 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
832 WHERE bi.backupid = ?
833 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
834
835 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
836
837 // Define id annotations (as final)
838
839 $member->annotate_ids('userfinal', 'userid');
840
841 // Define file annotations
842
64f93798 843 $group->annotate_files('group', 'description', 'id');
78d47b30 844 $group->annotate_files('group', 'icon', 'id');
5cc70f32 845 $grouping->annotate_files('grouping', 'description', 'id');
77547b46
EL
846
847 // Return the root element (groups)
848 return $groups;
849 }
850}
851
852/**
853 * structure step in charge of constructing the main users.xml file for all the users already
854 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
855 * overrides.
856 */
857class backup_users_structure_step extends backup_structure_step {
858
859 protected function define_structure() {
860 global $CFG;
861
862 // To know if we are anonymizing users
863 $anonymize = $this->get_setting_value('anonymize');
864 // To know if we are including role assignments
865 $roleassignments = $this->get_setting_value('role_assignments');
866
867 // Define each element separated
868
869 $users = new backup_nested_element('users');
870
871 // Create the array of user fields by hand, as far as we have various bits to control
872 // anonymize option, password backup, mnethostid...
873
874 // First, the fields not needing anonymization nor special handling
875 $normalfields = array(
876 'confirmed', 'policyagreed', 'deleted',
877 'lang', 'theme', 'timezone', 'firstaccess',
482aac65 878 'lastaccess', 'lastlogin', 'currentlogin',
77547b46
EL
879 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
880 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
881 'timemodified', 'trustbitmask', 'screenreader');
882
883 // Then, the fields potentially needing anonymization
884 $anonfields = array(
885 'username', 'idnumber', 'firstname', 'lastname',
482aac65
EL
886 'email', 'emailstop', 'icq', 'skype',
887 'yahoo', 'aim', 'msn', 'phone1',
888 'phone2', 'institution', 'department', 'address',
889 'city', 'country', 'lastip', 'picture',
c44d4aee 890 'url', 'description', 'descriptionformat', 'imagealt', 'auth');
77547b46
EL
891
892 // Add anonymized fields to $userfields with custom final element
893 foreach ($anonfields as $field) {
894 if ($anonymize) {
895 $userfields[] = new anonymizer_final_element($field);
896 } else {
897 $userfields[] = $field; // No anonymization, normally added
898 }
899 }
900
901 // mnethosturl requires special handling (custom final element)
902 $userfields[] = new mnethosturl_final_element('mnethosturl');
903
904 // password added conditionally
905 if (!empty($CFG->includeuserpasswordsinbackup)) {
906 $userfields[] = 'password';
907 }
908
909 // Merge all the fields
910 $userfields = array_merge($userfields, $normalfields);
911
912 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
913
914 $customfields = new backup_nested_element('custom_fields');
915
916 $customfield = new backup_nested_element('custom_field', array('id'), array(
917 'field_name', 'field_type', 'field_data'));
918
919 $tags = new backup_nested_element('tags');
920
921 $tag = new backup_nested_element('tag', array('id'), array(
922 'name', 'rawname'));
923
924 $preferences = new backup_nested_element('preferences');
925
926 $preference = new backup_nested_element('preference', array('id'), array(
927 'name', 'value'));
928
929 $roles = new backup_nested_element('roles');
930
931 $overrides = new backup_nested_element('role_overrides');
932
933 $override = new backup_nested_element('override', array('id'), array(
934 'roleid', 'capability', 'permission', 'timemodified',
935 'modifierid'));
936
937 $assignments = new backup_nested_element('role_assignments');
938
939 $assignment = new backup_nested_element('assignment', array('id'), array(
df997f84 940 'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
77547b46
EL
941 'sortorder'));
942
943 // Build the tree
944
945 $users->add_child($user);
946
947 $user->add_child($customfields);
948 $customfields->add_child($customfield);
949
950 $user->add_child($tags);
951 $tags->add_child($tag);
952
953 $user->add_child($preferences);
954 $preferences->add_child($preference);
955
956 $user->add_child($roles);
957
958 $roles->add_child($overrides);
959 $roles->add_child($assignments);
960
961 $overrides->add_child($override);
962 $assignments->add_child($assignment);
963
964 // Define sources
965
966 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
967 FROM {user} u
968 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
969 JOIN {context} c ON c.instanceid = u.id
970 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
971 WHERE bi.backupid = ?
972 AND bi.itemname = ?
973 AND c.contextlevel = ?', array(
c0bd6249
EL
974 backup_helper::is_sqlparam($this->get_backupid()),
975 backup_helper::is_sqlparam('userfinal'),
976 backup_helper::is_sqlparam(CONTEXT_USER)));
77547b46
EL
977
978 // All the rest on information is only added if we arent
979 // in an anonymized backup
980 if (!$anonymize) {
981 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
982 FROM {user_info_field} f
983 JOIN {user_info_data} d ON d.fieldid = f.id
984 WHERE d.userid = ?', array(backup::VAR_PARENTID));
985
986 $customfield->set_source_alias('shortname', 'field_name');
987 $customfield->set_source_alias('datatype', 'field_type');
988 $customfield->set_source_alias('data', 'field_data');
989
990 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
991 FROM {tag} t
992 JOIN {tag_instance} ti ON ti.tagid = t.id
993 WHERE ti.itemtype = ?
994 AND ti.itemid = ?', array(
c0bd6249 995 backup_helper::is_sqlparam('user'),
77547b46
EL
996 backup::VAR_PARENTID));
997
998 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
999
1000 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
1001
1002 // Assignments only added if specified
1003 if ($roleassignments) {
1004 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
1005 }
1006
1007 // Define id annotations (as final)
1008 $override->annotate_ids('rolefinal', 'roleid');
1009 }
1010
1011 // Return root element (users)
1012 return $users;
1013 }
1014}
1015
1016/**
1017 * structure step in charge of constructing the block.xml file for one
39b5371c 1018 * given block (instance and positions). If the block has custom DB structure
77547b46
EL
1019 * that will go to a separate file (different step defined in block class)
1020 */
1021class backup_block_instance_structure_step extends backup_structure_step {
1022
1023 protected function define_structure() {
1024 global $DB;
1025
1026 // Define each element separated
1027
5f8354eb 1028 $block = new backup_nested_element('block', array('id', 'contextid', 'version'), array(
61243f3a
EL
1029 'blockname', 'parentcontextid', 'showinsubcontexts', 'pagetypepattern',
1030 'subpagepattern', 'defaultregion', 'defaultweight', 'configdata'));
77547b46 1031
2d7cd798
EL
1032 $positions = new backup_nested_element('block_positions');
1033
1034 $position = new backup_nested_element('block_position', array('id'), array(
77547b46
EL
1035 'contextid', 'pagetype', 'subpage', 'visible',
1036 'region', 'weight'));
1037
1038 // Build the tree
1039
1040 $block->add_child($positions);
2d7cd798 1041 $positions->add_child($position);
77547b46
EL
1042
1043 // Transform configdata information if needed (process links and friends)
1044 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
1045 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
1046 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
1047 foreach ($configdata as $attribute => $value) {
1048 if (in_array($attribute, $attrstotransform)) {
1049 $configdata[$attribute] = $this->contenttransformer->process($value);
1050 }
1051 }
1052 $blockrec->configdata = base64_encode(serialize((object)$configdata));
1053 }
5f8354eb 1054 $blockrec->contextid = $this->task->get_contextid();
77547b46
EL
1055 // Get the version of the block
1056 $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
1057
1058 // Define sources
1059
1060 $block->set_source_array(array($blockrec));
1061
2d7cd798 1062 $position->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
77547b46 1063
4a15bb76
EL
1064 // File anotations (for fileareas specified on each block)
1065 foreach ($this->task->get_fileareas() as $filearea) {
1066 $block->annotate_files('block_' . $this->task->get_blockname(), $filearea, null);
1067 }
1068
77547b46
EL
1069 // Return the root element (block)
1070 return $block;
1071 }
1072}
1073
1074/**
1075 * structure step in charge of constructing the logs.xml file for all the log records found
1076 * in activity
1077 */
1078class backup_activity_logs_structure_step extends backup_structure_step {
1079
1080 protected function define_structure() {
1081
1082 // Define each element separated
1083
1084 $logs = new backup_nested_element('logs');
1085
1086 $log = new backup_nested_element('log', array('id'), array(
1087 'time', 'userid', 'ip', 'module',
1088 'action', 'url', 'info'));
1089
1090 // Build the tree
1091
1092 $logs->add_child($log);
1093
1094 // Define sources
1095
1096 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
1097
1098 // Annotations
1099 // NOTE: We don't annotate users from logs as far as they MUST be
1100 // always annotated by the activity.
1101
1102 // Return the root element (logs)
1103
1104 return $logs;
1105 }
1106}
1107
1108/**
1109 * structure in charge of constructing the inforef.xml file for all the items we want
1110 * to have referenced there (users, roles, files...)
1111 */
1112class backup_inforef_structure_step extends backup_structure_step {
1113
1114 protected function define_structure() {
1115
482aac65
EL
1116 // Items we want to include in the inforef file.
1117 $items = backup_helper::get_inforef_itemnames();
77547b46
EL
1118
1119 // Build the tree
1120
1121 $inforef = new backup_nested_element('inforef');
1122
1123 // For each item, conditionally, if there are already records, build element
1124 foreach ($items as $itemname) {
1125 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
1126 $elementroot = new backup_nested_element($itemname . 'ref');
482aac65 1127 $element = new backup_nested_element($itemname, array(), array('id'));
77547b46
EL
1128 $inforef->add_child($elementroot);
1129 $elementroot->add_child($element);
1130 $element->set_source_sql("
1131 SELECT itemid AS id
1132 FROM {backup_ids_temp}
1133 WHERE backupid = ?
1134 AND itemname = ?",
c0bd6249 1135 array(backup::VAR_BACKUPID, backup_helper::is_sqlparam($itemname)));
77547b46
EL
1136 }
1137 }
1138
1139 // We don't annotate anything there, but rely in the next step
1140 // (move_inforef_annotations_to_final) that will change all the
1141 // already saved 'inforref' entries to their 'final' annotations.
1142 return $inforef;
1143 }
1144}
1145
1146/**
1147 * This step will get all the annotations already processed to inforef.xml file and
1148 * transform them into 'final' annotations.
1149 */
1150class move_inforef_annotations_to_final extends backup_execution_step {
1151
1152 protected function define_execution() {
1153
482aac65
EL
1154 // Items we want to include in the inforef file
1155 $items = backup_helper::get_inforef_itemnames();
77547b46
EL
1156 foreach ($items as $itemname) {
1157 // Delegate to dbops
1158 backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
1159 }
1160 }
1161}
1162
1163/**
1164 * structure in charge of constructing the files.xml file with all the
1165 * annotated (final) files along the process. At, the same time, and
1166 * using one specialised nested_element, will copy them form moodle storage
1167 * to backup storage
1168 */
1169class backup_final_files_structure_step extends backup_structure_step {
1170
1171 protected function define_structure() {
1172
1173 // Define elements
1174
1175 $files = new backup_nested_element('files');
1176
1177 $file = new file_nested_element('file', array('id'), array(
64f93798 1178 'contenthash', 'contextid', 'component', 'filearea', 'itemid',
77547b46
EL
1179 'filepath', 'filename', 'userid', 'filesize',
1180 'mimetype', 'status', 'timecreated', 'timemodified',
1fd3ea43 1181 'source', 'author', 'license', 'sortorder'));
77547b46
EL
1182
1183 // Build the tree
1184
1185 $files->add_child($file);
1186
1187 // Define sources
1188
1189 $file->set_source_sql("SELECT f.*
1190 FROM {files} f
1191 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
1192 WHERE bi.backupid = ?
1193 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
1194
1195 return $files;
1196 }
1197}
1198
1199/**
1200 * Structure step in charge of creating the main moodle_backup.xml file
1201 * where all the information related to the backup, settings, license and
1202 * other information needed on restore is added*/
1203class backup_main_structure_step extends backup_structure_step {
1204
1205 protected function define_structure() {
1206
1207 global $CFG;
1208
1209 $info = array();
1210
1211 $info['name'] = $this->get_setting_value('filename');
1212 $info['moodle_version'] = $CFG->version;
1213 $info['moodle_release'] = $CFG->release;
1214 $info['backup_version'] = $CFG->backup_version;
1215 $info['backup_release'] = $CFG->backup_release;
1216 $info['backup_date'] = time();
1217 $info['backup_uniqueid']= $this->get_backupid();
c3ea499d 1218 $info['mnet_remoteusers']=backup_controller_dbops::backup_includes_mnet_remote_users($this->get_backupid());
77547b46 1219 $info['original_wwwroot']=$CFG->wwwroot;
482aac65 1220 $info['original_site_identifier_hash'] = md5(get_site_identifier());
77547b46 1221 $info['original_course_id'] = $this->get_courseid();
560811a9
EL
1222 $originalcourseinfo = backup_controller_dbops::backup_get_original_course_info($this->get_courseid());
1223 $info['original_course_fullname'] = $originalcourseinfo->fullname;
1224 $info['original_course_shortname'] = $originalcourseinfo->shortname;
1225 $info['original_course_startdate'] = $originalcourseinfo->startdate;
76cfb124 1226 $info['original_course_contextid'] = get_context_instance(CONTEXT_COURSE, $this->get_courseid())->id;
3a1cccc6 1227 $info['original_system_contextid'] = get_context_instance(CONTEXT_SYSTEM)->id;
77547b46
EL
1228
1229 // Get more information from controller
1230 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
1231
1232 // Define elements
1233
1234 $moodle_backup = new backup_nested_element('moodle_backup');
1235
1236 $information = new backup_nested_element('information', null, array(
1237 'name', 'moodle_version', 'moodle_release', 'backup_version',
c3ea499d 1238 'backup_release', 'backup_date', 'mnet_remoteusers', 'original_wwwroot',
560811a9
EL
1239 'original_site_identifier_hash', 'original_course_id',
1240 'original_course_fullname', 'original_course_shortname', 'original_course_startdate',
1241 'original_course_contextid', 'original_system_contextid'));
77547b46
EL
1242
1243 $details = new backup_nested_element('details');
1244
1245 $detail = new backup_nested_element('detail', array('backup_id'), array(
1246 'type', 'format', 'interactive', 'mode',
1247 'execution', 'executiontime'));
1248
1249 $contents = new backup_nested_element('contents');
1250
1251 $activities = new backup_nested_element('activities');
1252
1253 $activity = new backup_nested_element('activity', null, array(
1254 'moduleid', 'sectionid', 'modulename', 'title',
1255 'directory'));
1256
1257 $sections = new backup_nested_element('sections');
1258
1259 $section = new backup_nested_element('section', null, array(
1260 'sectionid', 'title', 'directory'));
1261
1262 $course = new backup_nested_element('course', null, array(
1263 'courseid', 'title', 'directory'));
1264
1265 $settings = new backup_nested_element('settings');
1266
1267 $setting = new backup_nested_element('setting', null, array(
d12fd69b 1268 'level', 'section', 'activity', 'name', 'value'));
77547b46
EL
1269
1270 // Build the tree
1271
1272 $moodle_backup->add_child($information);
1273
1274 $information->add_child($details);
1275 $details->add_child($detail);
1276
1277 $information->add_child($contents);
1278 if (!empty($cinfo['activities'])) {
1279 $contents->add_child($activities);
1280 $activities->add_child($activity);
1281 }
1282 if (!empty($cinfo['sections'])) {
1283 $contents->add_child($sections);
1284 $sections->add_child($section);
1285 }
1286 if (!empty($cinfo['course'])) {
1287 $contents->add_child($course);
1288 }
1289
1290 $information->add_child($settings);
1291 $settings->add_child($setting);
1292
1293
1294 // Set the sources
1295
1296 $information->set_source_array(array((object)$info));
1297
1298 $detail->set_source_array($dinfo);
1299
1300 $activity->set_source_array($cinfo['activities']);
1301
1302 $section->set_source_array($cinfo['sections']);
1303
1304 $course->set_source_array($cinfo['course']);
1305
1306 $setting->set_source_array($sinfo);
1307
1308 // Prepare some information to be sent to main moodle_backup.xml file
1309 return $moodle_backup;
1310 }
1311
1312}
1313
ce937f99 1314/**
12c80f79 1315 * Execution step that will generate the final zip (.mbz) file with all the contents
ce937f99
EL
1316 */
1317class backup_zip_contents extends backup_execution_step {
1318
1319 protected function define_execution() {
1320
1321 // Get basepath
1322 $basepath = $this->get_basepath();
1323
1324 // Get the list of files in directory
1325 $filestemp = get_directory_list($basepath, '', false, true, true);
1326 $files = array();
1327 foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1328 $files[$file] = $basepath . '/' . $file;
1329 }
1330
1331 // Add the log file if exists
1332 $logfilepath = $basepath . '.log';
1333 if (file_exists($logfilepath)) {
1334 $files['moodle_backup.log'] = $logfilepath;
1335 }
1336
12c80f79
EL
1337 // Calculate the zip fullpath (in OS temp area it's always backup.mbz)
1338 $zipfile = $basepath . '/backup.mbz';
ce937f99
EL
1339
1340 // Get the zip packer
1341 $zippacker = get_file_packer('application/zip');
1342
1343 // Zip files
1344 $zippacker->archive_to_pathname($files, $zipfile);
1345 }
1346}
1347
1348/**
1349 * This step will send the generated backup file to its final destination
1350 */
1351class backup_store_backup_file extends backup_execution_step {
1352
1353 protected function define_execution() {
1354
1355 // Get basepath
1356 $basepath = $this->get_basepath();
1357
12c80f79
EL
1358 // Calculate the zip fullpath (in OS temp area it's always backup.mbz)
1359 $zipfile = $basepath . '/backup.mbz';
ce937f99
EL
1360
1361 // Perform storage and return it (TODO: shouldn't be array but proper result object)
1362 return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
1363 }
1364}
1365
1366
77547b46
EL
1367/**
1368 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1369 * and put them to the backup_ids tables, to be used later as base to backup them
1370 */
1371class backup_activity_grade_items_to_ids extends backup_execution_step {
1372
1373 protected function define_execution() {
1374
1375 // Fetch all activity grade items
1376 if ($items = grade_item::fetch_all(array(
1377 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1378 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1379 // Annotate them in backup_ids
1380 foreach ($items as $item) {
1381 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1382 }
1383 }
1384 }
1385}
1386
1387/**
1388 * This step will annotate all the groups belonging to already annotated groupings
1389 */
1390class backup_annotate_groups_from_groupings extends backup_execution_step {
1391
1392 protected function define_execution() {
1393 global $DB;
1394
1395 // Fetch all the annotated groupings
1396 if ($groupings = $DB->get_records('backup_ids_temp', array(
1397 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1398 foreach ($groupings as $grouping) {
1399 if ($groups = $DB->get_records('groupings_groups', array(
1400 'groupingid' => $grouping->itemid))) {
1401 foreach ($groups as $group) {
1402 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1403 }
1404 }
1405 }
1406 }
1407 }
1408}
1409
1410/**
1411 * This step will annotate all the scales belonging to already annotated outcomes
1412 */
1413class backup_annotate_scales_from_outcomes extends backup_execution_step {
1414
1415 protected function define_execution() {
1416 global $DB;
1417
1418 // Fetch all the annotated outcomes
1419 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1420 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1421 foreach ($outcomes as $outcome) {
1422 if ($scale = $DB->get_record('grade_outcomes', array(
1423 'id' => $outcome->itemid))) {
1424 // Annotate as scalefinal because it's > 0
1425 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1426 }
1427 }
1428 }
1429 }
1430}
1431
1432/**
76cfb124 1433 * This step will generate all the file annotations for the already
39b5371c 1434 * annotated (final) users. Need to do this here because each user
77547b46
EL
1435 * has its own context and structure tasks only are able to handle
1436 * one context. Also, this step will guarantee that every user has
1437 * its context created (req for other steps)
1438 */
1439class backup_annotate_all_user_files extends backup_execution_step {
1440
1441 protected function define_execution() {
1442 global $DB;
1443
1444 // List of fileareas we are going to annotate
76cfb124
EL
1445 $fileareas = array('profile', 'icon');
1446
1447 if ($this->get_setting_value('user_files')) { // private files only if enabled in settings
1448 $fileareas[] = 'private';
1449 }
77547b46
EL
1450
1451 // Fetch all annotated (final) users
1452 $rs = $DB->get_recordset('backup_ids_temp', array(
1453 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
1454 foreach ($rs as $record) {
1455 $userid = $record->itemid;
1456 $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
1457 // Proceed with every user filearea
1458 foreach ($fileareas as $filearea) {
78d47b30 1459 // We don't need to specify itemid ($userid - 5th param) as far as by
77547b46 1460 // context we can get all the associated files. See MDL-22092
64f93798 1461 backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, 'user', $filearea, null);
77547b46
EL
1462 }
1463 }
1464 $rs->close();
1465 }
1466}
1467
1468/**
1469 * structure step in charge of constructing the grades.xml file for all the grade items
1470 * and letters related to one activity
1471 */
1472class backup_activity_grades_structure_step extends backup_structure_step {
1473
1474 protected function define_structure() {
1475
1476 // To know if we are including userinfo
1477 $userinfo = $this->get_setting_value('userinfo');
1478
1479 // Define each element separated
1480
1481 $book = new backup_nested_element('activity_gradebook');
1482
1483 $items = new backup_nested_element('grade_items');
1484
1485 $item = new backup_nested_element('grade_item', array('id'), array(
1486 'categoryid', 'itemname', 'itemtype', 'itemmodule',
1487 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
1488 'calculation', 'gradetype', 'grademax', 'grademin',
1489 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
1490 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
1491 'decimals', 'hidden', 'locked', 'locktime',
1492 'needsupdate', 'timecreated', 'timemodified'));
1493
1494 $grades = new backup_nested_element('grade_grades');
1495
1496 $grade = new backup_nested_element('grade_grade', array('id'), array(
1497 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
1498 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
1499 'locked', 'locktime', 'exported', 'overridden',
1500 'excluded', 'feedback', 'feedbackformat', 'information',
1501 'informationformat', 'timecreated', 'timemodified'));
1502
1503 $letters = new backup_nested_element('grade_letters');
1504
1505 $letter = new backup_nested_element('grade_letter', 'id', array(
1506 'lowerboundary', 'letter'));
1507
1508 // Build the tree
1509
1510 $book->add_child($items);
1511 $items->add_child($item);
1512
1513 $item->add_child($grades);
1514 $grades->add_child($grade);
1515
1516 $book->add_child($letters);
1517 $letters->add_child($letter);
1518
1519 // Define sources
1520
315f6d8e
AD
1521 $item->set_source_sql("SELECT gi.*
1522 FROM {grade_items} gi
1523 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
1524 WHERE bi.backupid = ?
1525 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
77547b46
EL
1526
1527 // This only happens if we are including user info
1528 if ($userinfo) {
1529 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
1530 }
1531
1532 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
1533
1534 // Annotations
1535
1536 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
1537 $item->annotate_ids('outcome', 'outcomeid');
1538
1539 $grade->annotate_ids('user', 'userid');
1540 $grade->annotate_ids('user', 'usermodified');
1541
1542 // Return the root element (book)
1543
1544 return $book;
1545 }
1546}
bd39b6f2
SH
1547
1548/**
1549 * Backups up the course completion information for the course.
1550 */
1551class backup_course_completion_structure_step extends backup_structure_step {
1552
1553 protected function execute_condition() {
1554 // Check that all activities have been included
1555 if ($this->task->is_excluding_activities()) {
1556 return false;
1557 }
1558 return true;
1559 }
1560
1561 /**
1562 * The structure of the course completion backup
1563 *
1564 * @return backup_nested_element
1565 */
1566 protected function define_structure() {
1567
1568 // To know if we are including user completion info
1569 $userinfo = $this->get_setting_value('userscompletion');
1570
1571 $cc = new backup_nested_element('course_completion');
1572
1573 $criteria = new backup_nested_element('course_completion_criteria', array('id'), array(
1574 'course','criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod', 'timeend', 'gradepass', 'role'
1575 ));
1576
1577 $criteriacompletions = new backup_nested_element('course_completion_crit_completions');
1578
1579 $criteriacomplete = new backup_nested_element('course_completion_crit_compl', array('id'), array(
1580 'criteriaid', 'userid','gradefinal','unenrolled','deleted','timecompleted'
1581 ));
1582
1583 $coursecompletions = new backup_nested_element('course_completions', array('id'), array(
1584 'userid', 'course', 'deleted', 'timenotified', 'timeenrolled','timestarted','timecompleted','reaggregate'
1585 ));
1586
1587 $notify = new backup_nested_element('course_completion_notify', array('id'), array(
1588 'course','role','message','timesent'
1589 ));
1590
1591 $aggregatemethod = new backup_nested_element('course_completion_aggr_methd', array('id'), array(
1592 'course','criteriatype','method','value'
1593 ));
1594
1595 $cc->add_child($criteria);
1596 $criteria->add_child($criteriacompletions);
1597 $criteriacompletions->add_child($criteriacomplete);
1598 $cc->add_child($coursecompletions);
1599 $cc->add_child($notify);
1600 $cc->add_child($aggregatemethod);
1601
1602 // We need to get the courseinstances shortname rather than an ID for restore
1603 $criteria->set_source_sql("SELECT ccc.*, c.shortname courseinstanceshortname
1604 FROM {course_completion_criteria} ccc
1605 LEFT JOIN {course} c ON c.id = ccc.courseinstance
1606 WHERE ccc.course = ?", array(backup::VAR_COURSEID));
1607
1608
1609 $notify->set_source_table('course_completion_notify', array('course' => backup::VAR_COURSEID));
1610 $aggregatemethod->set_source_table('course_completion_aggr_methd', array('course' => backup::VAR_COURSEID));
1611
1612 if ($userinfo) {
1613 $criteriacomplete->set_source_table('course_completion_crit_compl', array('criteriaid' => backup::VAR_PARENTID));
1614 $coursecompletions->set_source_table('course_completions', array('course' => backup::VAR_COURSEID));
1615 }
1616
1617 $criteria->annotate_ids('role', 'role');
1618 $criteriacomplete->annotate_ids('user', 'userid');
1619 $coursecompletions->annotate_ids('user', 'userid');
1620 $notify->annotate_ids('role', 'role');
1621
1622 return $cc;
1623
1624 }
1625}