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