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