MDL-52252 Tags: Add tags to modules (Resources and Activities)
[moodle.git] / backup / moodle2 / backup_stepslib.php
CommitLineData
77547b46
EL
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
58b322cf
DM
19 * Defines various backup steps that will be used by common tasks in backup
20 *
21 * @package core_backup
22 * @subpackage moodle2
23 * @category backup
24 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
77547b46
EL
26 */
27
58b322cf 28defined('MOODLE_INTERNAL') || die();
2de3539b
EL
29
30/**
1f3b2356 31 * Create the temp dir where backup/restore will happen and create temp ids table.
2de3539b 32 */
77547b46
EL
33class create_and_clean_temp_stuff extends backup_execution_step {
34
35 protected function define_execution() {
1cd39657 36 $progress = $this->task->get_progress();
37 $progress->start_progress('Deleting backup directories');
77547b46 38 backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
1cd39657 39 backup_helper::clear_backup_dir($this->get_backupid(), $progress); // Empty temp dir, just in case
1b55bdb6 40 backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
77547b46 41 backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
1cd39657 42 $progress->end_progress();
77547b46
EL
43 }
44}
45
2de3539b 46/**
1f3b2356
EM
47 * Delete the temp dir used by backup/restore (conditionally),
48 * delete old directories and drop temp ids table. Note we delete
39b5371c 49 * the directory but not the corresponding log file that will be
1f3b2356
EM
50 * there for, at least, 1 week - only delete_old_backup_dirs() or cron
51 * deletes log files (for easier access to them).
2de3539b
EL
52 */
53class drop_and_clean_temp_stuff extends backup_execution_step {
54
dc1e4cce
EL
55 protected $skipcleaningtempdir = false;
56
2de3539b
EL
57 protected function define_execution() {
58 global $CFG;
dc1e4cce 59
2de3539b 60 backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
1f3b2356 61 backup_helper::delete_old_backup_dirs(strtotime('-1 week')); // Delete > 1 week old temp dirs.
dc1e4cce
EL
62 // Delete temp dir conditionally:
63 // 1) If $CFG->keeptempdirectoriesonbackup is not enabled
64 // 2) If backup temp dir deletion has been marked to be avoided
65 if (empty($CFG->keeptempdirectoriesonbackup) && !$this->skipcleaningtempdir) {
1cd39657 66 $progress = $this->task->get_progress();
67 $progress->start_progress('Deleting backup dir');
68 backup_helper::delete_backup_dir($this->get_backupid(), $progress); // Empty backup dir
69 $progress->end_progress();
2de3539b
EL
70 }
71 }
dc1e4cce
EL
72
73 public function skip_cleaning_temp_dir($skip) {
74 $this->skipcleaningtempdir = $skip;
75 }
2de3539b
EL
76}
77
77547b46
EL
78/**
79 * Create the directory where all the task (activity/block...) information will be stored
80 */
81class create_taskbasepath_directory extends backup_execution_step {
82
83 protected function define_execution() {
84 global $CFG;
85 $basepath = $this->task->get_taskbasepath();
86 if (!check_dir_exists($basepath, true, true)) {
87 throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
88 }
89 }
90}
91
92/**
39b5371c 93 * Abstract structure step, parent of all the activity structure steps. Used to wrap the
ba66edd0 94 * activity structure definition within the main <activity ...> tag.
77547b46
EL
95 */
96abstract class backup_activity_structure_step extends backup_structure_step {
97
4abf04ea
EL
98 /**
99 * Wraps any activity backup structure within the common 'activity' element
100 * that will include common to all activities information like id, context...
a75af402
DM
101 *
102 * @param backup_nested_element $activitystructure the element to wrap
103 * @return backup_nested_element the $activitystructure wrapped by the common 'activity' element
4abf04ea 104 */
77547b46
EL
105 protected function prepare_activity_structure($activitystructure) {
106
107 // Create the wrap element
108 $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
109
110 // Build the tree
111 $activity->add_child($activitystructure);
112
113 // Set the source
114 $activityarr = array((object)array(
115 'id' => $this->task->get_activityid(),
116 'moduleid' => $this->task->get_moduleid(),
117 'modulename' => $this->task->get_modulename(),
118 'contextid' => $this->task->get_contextid()));
119
120 $activity->set_source_array($activityarr);
121
122 // Return the root element (activity)
123 return $activity;
124 }
125}
126
767cb7f0
EL
127/**
128 * Abstract structure step, to be used by all the activities using core questions stuff
41941110 129 * (namely quiz module), supporting question plugins, states and sessions
767cb7f0
EL
130 */
131abstract class backup_questions_activity_structure_step extends backup_activity_structure_step {
132
133 /**
134 * Attach to $element (usually attempts) the needed backup structures
bea1a6a7 135 * for question_usages and all the associated data.
3e3ae0ee
TH
136 *
137 * @param backup_nested_element $element the element that will contain all the question_usages data.
138 * @param string $usageidname the name of the element that holds the usageid.
139 * This must be child of $element, and must be a final element.
140 * @param string $nameprefix this prefix is added to all the element names we create.
141 * Element names in the XML must be unique, so if you are using usages in
142 * two different ways, you must give a prefix to at least one of them. If
143 * you only use one sort of usage, then you can just use the default empty prefix.
607b0a70 144 * This should include a trailing underscore. For example "myprefix_"
767cb7f0 145 */
3e3ae0ee 146 protected function add_question_usages($element, $usageidname, $nameprefix = '') {
d6522c33
TH
147 global $CFG;
148 require_once($CFG->dirroot . '/question/engine/lib.php');
149
767cb7f0
EL
150 // Check $element is one nested_backup_element
151 if (! $element instanceof backup_nested_element) {
152 throw new backup_step_exception('question_states_bad_parent_element', $element);
153 }
d6522c33
TH
154 if (! $element->get_final_element($usageidname)) {
155 throw new backup_step_exception('question_states_bad_question_attempt_element', $usageidname);
767cb7f0
EL
156 }
157
3e3ae0ee 158 $quba = new backup_nested_element($nameprefix . 'question_usage', array('id'),
c749527b 159 array('component', 'preferredbehaviour'));
767cb7f0 160
3e3ae0ee
TH
161 $qas = new backup_nested_element($nameprefix . 'question_attempts');
162 $qa = new backup_nested_element($nameprefix . 'question_attempt', array('id'), array(
3f87a74b 163 'slot', 'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction',
bea1a6a7
TH
164 'flagged', 'questionsummary', 'rightanswer', 'responsesummary',
165 'timemodified'));
767cb7f0 166
3e3ae0ee
TH
167 $steps = new backup_nested_element($nameprefix . 'steps');
168 $step = new backup_nested_element($nameprefix . 'step', array('id'), array(
bea1a6a7 169 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid'));
767cb7f0 170
3e3ae0ee
TH
171 $response = new backup_nested_element($nameprefix . 'response');
172 $variable = new backup_nested_element($nameprefix . 'variable', null, array('name', 'value'));
767cb7f0
EL
173
174 // Build the tree
bea1a6a7
TH
175 $element->add_child($quba);
176 $quba->add_child($qas);
177 $qas->add_child($qa);
178 $qa->add_child($steps);
179 $steps->add_child($step);
c749527b
TH
180 $step->add_child($response);
181 $response->add_child($variable);
767cb7f0
EL
182
183 // Set the sources
bea1a6a7 184 $quba->set_source_table('question_usages',
d6522c33 185 array('id' => '../' . $usageidname));
52514503
ARN
186 $qa->set_source_table('question_attempts', array('questionusageid' => backup::VAR_PARENTID), 'slot ASC');
187 $step->set_source_table('question_attempt_steps', array('questionattemptid' => backup::VAR_PARENTID), 'sequencenumber ASC');
188 $variable->set_source_table('question_attempt_step_data', array('attemptstepid' => backup::VAR_PARENTID));
767cb7f0
EL
189
190 // Annotate ids
bea1a6a7 191 $qa->annotate_ids('question', 'questionid');
c749527b 192 $step->annotate_ids('user', 'userid');
767cb7f0
EL
193
194 // Annotate files
c749527b 195 $fileareas = question_engine::get_all_response_file_areas();
bea1a6a7
TH
196 foreach ($fileareas as $filearea) {
197 $step->annotate_files('question', $filearea, 'id');
198 }
767cb7f0
EL
199 }
200}
201
bea1a6a7 202
767cb7f0
EL
203/**
204 * backup structure step in charge of calculating the categories to be
205 * included in backup, based in the context being backuped (module/course)
206 * and the already annotated questions present in backup_ids_temp
207 */
208class backup_calculate_question_categories extends backup_execution_step {
209
210 protected function define_execution() {
211 backup_question_dbops::calculate_question_categories($this->get_backupid(), $this->task->get_contextid());
212 }
213}
214
215/**
216 * backup structure step in charge of deleting all the questions annotated
217 * in the backup_ids_temp table
218 */
219class backup_delete_temp_questions extends backup_execution_step {
220
221 protected function define_execution() {
222 backup_question_dbops::delete_temp_questions($this->get_backupid());
223 }
224}
225
77547b46 226/**
39b5371c 227 * Abstract structure step, parent of all the block structure steps. Used to wrap the
77547b46
EL
228 * block structure definition within the main <block ...> tag
229 */
230abstract class backup_block_structure_step extends backup_structure_step {
231
232 protected function prepare_block_structure($blockstructure) {
233
234 // Create the wrap element
235 $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
236
237 // Build the tree
238 $block->add_child($blockstructure);
239
240 // Set the source
241 $blockarr = array((object)array(
242 'id' => $this->task->get_blockid(),
243 'blockname' => $this->task->get_blockname(),
244 'contextid' => $this->task->get_contextid()));
245
246 $block->set_source_array($blockarr);
247
248 // Return the root element (block)
249 return $block;
250 }
251}
252
253/**
254 * structure step that will generate the module.xml file for the activity,
39b5371c 255 * accumulating various information about the activity, annotating groupings
77547b46
EL
256 * and completion/avail conf
257 */
258class backup_module_structure_step extends backup_structure_step {
259
260 protected function define_structure() {
bde002b8 261 global $DB;
77547b46
EL
262
263 // Define each element separated
264
265 $module = new backup_nested_element('module', array('id', 'version'), array(
266 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
267 'added', 'score', 'indent', 'visible',
dea081af 268 'visibleold', 'groupmode', 'groupingid',
77547b46 269 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
38674ef9 270 'availability', 'showdescription'));
77547b46 271
dffcf46f
NK
272 $tags = new backup_nested_element('tags');
273 $tag = new backup_nested_element('tag', array('id'), array('name', 'rawname'));
274
a90659d6
EL
275 // attach format plugin structure to $module element, only one allowed
276 $this->add_plugin_structure('format', $module, false);
277
3d738ce7
SH
278 // attach plagiarism plugin structure to $module element, there can be potentially
279 // many plagiarism plugins storing information about this course
040aa088 280 $this->add_plugin_structure('plagiarism', $module, true);
571ae252 281
a66e8bb3
SC
282 // attach local plugin structure to $module, multiple allowed
283 $this->add_plugin_structure('local', $module, true);
284
dffcf46f
NK
285 $module->add_child($tags);
286 $tags->add_child($tag);
287
77547b46 288 // Set the sources
bde002b8
PS
289 $concat = $DB->sql_concat("'mod_'", 'm.name');
290 $module->set_source_sql("
291 SELECT cm.*, cp.value AS version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
77547b46
EL
292 FROM {course_modules} cm
293 JOIN {modules} m ON m.id = cm.module
bde002b8 294 JOIN {config_plugins} cp ON cp.plugin = $concat AND cp.name = 'version'
77547b46 295 JOIN {course_sections} s ON s.id = cm.section
bde002b8 296 WHERE cm.id = ?", array(backup::VAR_MODID));
77547b46 297
dffcf46f
NK
298 $tag->set_source_sql("SELECT t.id, t.name, t.rawname
299 FROM {tag} t
300 JOIN {tag_instance} ti ON ti.tagid = t.id
301 WHERE ti.itemtype = 'course_modules'
302 AND ti.component = 'core'
303 AND ti.itemid = ?", array(backup::VAR_MODID));
304
77547b46
EL
305 // Define annotations
306 $module->annotate_ids('grouping', 'groupingid');
307
308 // Return the root element ($module)
309 return $module;
310 }
311}
312
313/**
39b5371c 314 * structure step that will generate the section.xml file for the section
77547b46
EL
315 * annotating files
316 */
317class backup_section_structure_step extends backup_structure_step {
318
319 protected function define_structure() {
320
321 // Define each element separated
322
323 $section = new backup_nested_element('section', array('id'), array(
ce4dfd27 324 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible',
38674ef9 325 'availabilityjson'));
77547b46 326
a90659d6
EL
327 // attach format plugin structure to $section element, only one allowed
328 $this->add_plugin_structure('format', $section, false);
329
a66e8bb3
SC
330 // attach local plugin structure to $section element, multiple allowed
331 $this->add_plugin_structure('local', $section, true);
332
03604430
MG
333 // Add nested elements for course_format_options table
334 $formatoptions = new backup_nested_element('course_format_options', array('id'), array(
335 'format', 'name', 'value'));
336 $section->add_child($formatoptions);
337
38674ef9 338 // Define sources.
452ef534 339 $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
03604430 340 $formatoptions->set_source_sql('SELECT cfo.id, cfo.format, cfo.name, cfo.value
92d38668
MG
341 FROM {course} c
342 JOIN {course_format_options} cfo
343 ON cfo.courseid = c.id AND cfo.format = c.format
344 WHERE c.id = ? AND cfo.sectionid = ?',
345 array(backup::VAR_COURSEID, backup::VAR_SECTIONID));
77547b46 346
cd00f9b7
EL
347 // Aliases
348 $section->set_source_alias('section', 'number');
452ef534
DP
349 // The 'availability' field needs to be renamed because it clashes with
350 // the old nested element structure for availability data.
351 $section->set_source_alias('availability', 'availabilityjson');
cd00f9b7 352
77547b46 353 // Set annotations
64f93798 354 $section->annotate_files('course', 'section', 'id');
77547b46
EL
355
356 return $section;
357 }
358}
359
360/**
361 * structure step that will generate the course.xml file for the course, including
df997f84 362 * course category reference, tags, modules restriction information
77547b46
EL
363 * and some annotations (files & groupings)
364 */
365class backup_course_structure_step extends backup_structure_step {
366
367 protected function define_structure() {
368 global $DB;
369
370 // Define each element separated
371
372 $course = new backup_nested_element('course', array('id', 'contextid'), array(
df997f84 373 'shortname', 'fullname', 'idnumber',
b5cf83f0
MG
374 'summary', 'summaryformat', 'format', 'showgrades',
375 'newsitems', 'startdate',
58f8ca39 376 'marker', 'maxbytes', 'legacyfiles', 'showreports',
b5cf83f0 377 'visible', 'groupmode', 'groupmodeforce',
df997f84
PS
378 'defaultgroupingid', 'lang', 'theme',
379 'timecreated', 'timemodified',
9665ecd2 380 'requested',
395dae30 381 'enablecompletion', 'completionstartonenrol', 'completionnotify'));
77547b46
EL
382
383 $category = new backup_nested_element('category', array('id'), array(
384 'name', 'description'));
385
386 $tags = new backup_nested_element('tags');
387
388 $tag = new backup_nested_element('tag', array('id'), array(
389 'name', 'rawname'));
390
4fda584f
EL
391 // attach format plugin structure to $course element, only one allowed
392 $this->add_plugin_structure('format', $course, false);
393
89213d00 394 // attach theme plugin structure to $course element; multiple themes can
395 // save course data (in case of user theme, legacy theme, etc)
396 $this->add_plugin_structure('theme', $course, true);
397
158e9e2a
PS
398 // attach general report plugin structure to $course element; multiple
399 // reports can save course data if required
400 $this->add_plugin_structure('report', $course, true);
401
5e0dae12 402 // attach course report plugin structure to $course element; multiple
403 // course reports can save course data if required
404 $this->add_plugin_structure('coursereport', $course, true);
405
3d738ce7
SH
406 // attach plagiarism plugin structure to $course element, there can be potentially
407 // many plagiarism plugins storing information about this course
040aa088 408 $this->add_plugin_structure('plagiarism', $course, true);
571ae252 409
a66e8bb3
SC
410 // attach local plugin structure to $course element; multiple local plugins
411 // can save course data if required
412 $this->add_plugin_structure('local', $course, true);
413
77547b46
EL
414 // Build the tree
415
416 $course->add_child($category);
417
418 $course->add_child($tags);
419 $tags->add_child($tag);
420
77547b46
EL
421 // Set the sources
422
423 $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
424 $courserec->contextid = $this->task->get_contextid();
425
d6cd57de
MG
426 $formatoptions = course_get_format($courserec)->get_format_options();
427 $course->add_final_elements(array_keys($formatoptions));
428 foreach ($formatoptions as $key => $value) {
429 $courserec->$key = $value;
430 }
431
77547b46
EL
432 $course->set_source_array(array($courserec));
433
434 $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
435
436 $category->set_source_array(array($categoryrec));
437
438 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
439 FROM {tag} t
440 JOIN {tag_instance} ti ON ti.tagid = t.id
441 WHERE ti.itemtype = ?
442 AND ti.itemid = ?', array(
c0bd6249 443 backup_helper::is_sqlparam('course'),
77547b46
EL
444 backup::VAR_PARENTID));
445
77547b46
EL
446 // Some annotations
447
77547b46
EL
448 $course->annotate_ids('grouping', 'defaultgroupingid');
449
64f93798 450 $course->annotate_files('course', 'summary', null);
d1f8c1bd 451 $course->annotate_files('course', 'overviewfiles', null);
64f93798 452 $course->annotate_files('course', 'legacy', null);
77547b46
EL
453
454 // Return root element ($course)
455
456 return $course;
457 }
458}
459
cb34c4cd
PS
460/**
461 * structure step that will generate the enrolments.xml file for the given course
462 */
463class backup_enrolments_structure_step extends backup_structure_step {
464
03c39daa
DM
465 /**
466 * Skip enrolments on the front page.
467 * @return bool
468 */
469 protected function execute_condition() {
470 return ($this->get_courseid() != SITEID);
471 }
472
cb34c4cd
PS
473 protected function define_structure() {
474
475 // To know if we are including users
476 $users = $this->get_setting_value('users');
477
478 // Define each element separated
479
480 $enrolments = new backup_nested_element('enrolments');
481
482 $enrols = new backup_nested_element('enrols');
483
484 $enrol = new backup_nested_element('enrol', array('id'), array(
7a7b8a1f 485 'enrol', 'status', 'name', 'enrolperiod', 'enrolstartdate',
16dfc326 486 'enrolenddate', 'expirynotify', 'expirythreshold', 'notifyall',
882fb835
PS
487 'password', 'cost', 'currency', 'roleid',
488 'customint1', 'customint2', 'customint3', 'customint4', 'customint5', 'customint6', 'customint7', 'customint8',
489 'customchar1', 'customchar2', 'customchar3',
490 'customdec1', 'customdec2',
491 'customtext1', 'customtext2', 'customtext3', 'customtext4',
492 'timecreated', 'timemodified'));
cb34c4cd
PS
493
494 $userenrolments = new backup_nested_element('user_enrolments');
495
496 $enrolment = new backup_nested_element('enrolment', array('id'), array(
101bd9c9 497 'status', 'userid', 'timestart', 'timeend', 'modifierid',
cb34c4cd
PS
498 'timemodified'));
499
500 // Build the tree
501 $enrolments->add_child($enrols);
502 $enrols->add_child($enrol);
503 $enrol->add_child($userenrolments);
504 $userenrolments->add_child($enrolment);
505
7a7b8a1f 506 // Define sources - the instances are restored using the same sortorder, we do not need to store it in xml and deal with it afterwards.
52514503 507 $enrol->set_source_table('enrol', array('courseid' => backup::VAR_COURSEID), 'sortorder ASC');
cb34c4cd 508
101bd9c9 509 // User enrolments only added only if users included
cb34c4cd
PS
510 if ($users) {
511 $enrolment->set_source_table('user_enrolments', array('enrolid' => backup::VAR_PARENTID));
02eca29c 512 $enrolment->annotate_ids('user', 'userid');
cb34c4cd
PS
513 }
514
902944a8
PS
515 $enrol->annotate_ids('role', 'roleid');
516
f6199295
MP
517 // Add enrol plugin structure.
518 $this->add_plugin_structure('enrol', $enrol, false);
cb34c4cd
PS
519
520 return $enrolments;
521 }
522}
523
77547b46
EL
524/**
525 * structure step that will generate the roles.xml file for the given context, observing
526 * the role_assignments setting to know if that part needs to be included
527 */
528class backup_roles_structure_step extends backup_structure_step {
529
530 protected function define_structure() {
531
532 // To know if we are including role assignments
533 $roleassignments = $this->get_setting_value('role_assignments');
534
535 // Define each element separated
536
537 $roles = new backup_nested_element('roles');
538
539 $overrides = new backup_nested_element('role_overrides');
540
541 $override = new backup_nested_element('override', array('id'), array(
542 'roleid', 'capability', 'permission', 'timemodified',
543 'modifierid'));
544
545 $assignments = new backup_nested_element('role_assignments');
546
547 $assignment = new backup_nested_element('assignment', array('id'), array(
cb34c4cd 548 'roleid', 'userid', 'timemodified', 'modifierid', 'component', 'itemid',
77547b46
EL
549 'sortorder'));
550
551 // Build the tree
552 $roles->add_child($overrides);
553 $roles->add_child($assignments);
554
555 $overrides->add_child($override);
556 $assignments->add_child($assignment);
557
558 // Define sources
559
560 $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
561
562 // Assignments only added if specified
563 if ($roleassignments) {
564 $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
565 }
566
567 // Define id annotations
568 $override->annotate_ids('role', 'roleid');
569
570 $assignment->annotate_ids('role', 'roleid');
571
572 $assignment->annotate_ids('user', 'userid');
573
cb34c4cd
PS
574 //TODO: how do we annotate the itemid? the meaning depends on the content of component table (skodak)
575
77547b46
EL
576 return $roles;
577 }
578}
579
580/**
581 * structure step that will generate the roles.xml containing the
582 * list of roles used along the whole backup process. Just raw
583 * list of used roles from role table
584 */
585class backup_final_roles_structure_step extends backup_structure_step {
586
587 protected function define_structure() {
588
589 // Define elements
590
591 $rolesdef = new backup_nested_element('roles_definition');
592
593 $role = new backup_nested_element('role', array('id'), array(
594 'name', 'shortname', 'nameincourse', 'description',
595 'sortorder', 'archetype'));
596
597 // Build the tree
598
599 $rolesdef->add_child($role);
600
601 // Define sources
602
603 $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
604 FROM {role} r
605 JOIN {backup_ids_temp} bi ON r.id = bi.itemid
606 LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
607 WHERE bi.backupid = ?
608 AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
609
610 // Return main element (rolesdef)
611 return $rolesdef;
612 }
613}
614
615/**
616 * structure step that will generate the scales.xml containing the
617 * list of scales used along the whole backup process.
618 */
619class backup_final_scales_structure_step extends backup_structure_step {
620
621 protected function define_structure() {
622
623 // Define elements
624
625 $scalesdef = new backup_nested_element('scales_definition');
626
627 $scale = new backup_nested_element('scale', array('id'), array(
628 'courseid', 'userid', 'name', 'scale',
629 'description', 'descriptionformat', 'timemodified'));
630
631 // Build the tree
632
633 $scalesdef->add_child($scale);
634
635 // Define sources
636
637 $scale->set_source_sql("SELECT s.*
638 FROM {scale} s
639 JOIN {backup_ids_temp} bi ON s.id = bi.itemid
640 WHERE bi.backupid = ?
641 AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
642
3a1cccc6 643 // Annotate scale files (they store files in system context, so pass it instead of default one)
a689cd1d 644 $scale->annotate_files('grade', 'scale', 'id', context_system::instance()->id);
3a1cccc6 645
77547b46
EL
646 // Return main element (scalesdef)
647 return $scalesdef;
648 }
649}
650
651/**
652 * structure step that will generate the outcomes.xml containing the
653 * list of outcomes used along the whole backup process.
654 */
655class backup_final_outcomes_structure_step extends backup_structure_step {
656
657 protected function define_structure() {
658
659 // Define elements
660
661 $outcomesdef = new backup_nested_element('outcomes_definition');
662
663 $outcome = new backup_nested_element('outcome', array('id'), array(
664 'courseid', 'userid', 'shortname', 'fullname',
665 'scaleid', 'description', 'descriptionformat', 'timecreated',
666 'timemodified','usermodified'));
667
668 // Build the tree
669
670 $outcomesdef->add_child($outcome);
671
672 // Define sources
673
674 $outcome->set_source_sql("SELECT o.*
675 FROM {grade_outcomes} o
676 JOIN {backup_ids_temp} bi ON o.id = bi.itemid
677 WHERE bi.backupid = ?
678 AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
679
c8730ff0 680 // Annotate outcome files (they store files in system context, so pass it instead of default one)
a689cd1d 681 $outcome->annotate_files('grade', 'outcome', 'id', context_system::instance()->id);
c8730ff0 682
77547b46
EL
683 // Return main element (outcomesdef)
684 return $outcomesdef;
685 }
686}
687
688/**
689 * structure step in charge of constructing the filters.xml file for all the filters found
690 * in activity
691 */
692class backup_filters_structure_step extends backup_structure_step {
693
694 protected function define_structure() {
695
696 // Define each element separated
697
698 $filters = new backup_nested_element('filters');
699
700 $actives = new backup_nested_element('filter_actives');
701
702 $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
703
704 $configs = new backup_nested_element('filter_configs');
705
706 $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
707
708 // Build the tree
709
710 $filters->add_child($actives);
711 $filters->add_child($configs);
712
713 $actives->add_child($active);
714 $configs->add_child($config);
715
716 // Define sources
717
718 list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
719
720 $active->set_source_array($activearr);
721 $config->set_source_array($configarr);
722
723 // Return the root element (filters)
724 return $filters;
725 }
726}
727
728/**
729 * structure step in charge of constructing the comments.xml file for all the comments found
730 * in a given context
731 */
732class backup_comments_structure_step extends backup_structure_step {
733
734 protected function define_structure() {
735
736 // Define each element separated
737
738 $comments = new backup_nested_element('comments');
739
740 $comment = new backup_nested_element('comment', array('id'), array(
741 'commentarea', 'itemid', 'content', 'format',
742 'userid', 'timecreated'));
743
744 // Build the tree
745
746 $comments->add_child($comment);
747
748 // Define sources
749
750 $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
751
752 // Define id annotations
753
754 $comment->annotate_ids('user', 'userid');
755
756 // Return the root element (comments)
757 return $comments;
758 }
759}
760
2832093f
YB
761/**
762 * structure step in charge of constructing the badges.xml file for all the badges found
763 * in a given context
764 */
765class backup_badges_structure_step extends backup_structure_step {
766
767 protected function execute_condition() {
768 // Check that all activities have been included.
769 if ($this->task->is_excluding_activities()) {
770 return false;
771 }
772 return true;
773 }
774
775 protected function define_structure() {
776
777 // Define each element separated.
778
779 $badges = new backup_nested_element('badges');
780 $badge = new backup_nested_element('badge', array('id'), array('name', 'description',
781 'timecreated', 'timemodified', 'usercreated', 'usermodified', 'issuername',
782 'issuerurl', 'issuercontact', 'expiredate', 'expireperiod', 'type', 'courseid',
783 'message', 'messagesubject', 'attachment', 'notification', 'status', 'nextcron'));
784
785 $criteria = new backup_nested_element('criteria');
786 $criterion = new backup_nested_element('criterion', array('id'), array('badgeid',
fffeb03f 787 'criteriatype', 'method', 'description', 'descriptionformat'));
2832093f
YB
788
789 $parameters = new backup_nested_element('parameters');
790 $parameter = new backup_nested_element('parameter', array('id'), array('critid',
791 'name', 'value', 'criteriatype'));
792
bd0d2bdc
YB
793 $manual_awards = new backup_nested_element('manual_awards');
794 $manual_award = new backup_nested_element('manual_award', array('id'), array('badgeid',
795 'recipientid', 'issuerid', 'issuerrole', 'datemet'));
796
2832093f
YB
797 // Build the tree.
798
799 $badges->add_child($badge);
800 $badge->add_child($criteria);
801 $criteria->add_child($criterion);
802 $criterion->add_child($parameters);
803 $parameters->add_child($parameter);
bd0d2bdc
YB
804 $badge->add_child($manual_awards);
805 $manual_awards->add_child($manual_award);
2832093f
YB
806
807 // Define sources.
808
809 $badge->set_source_table('badge', array('courseid' => backup::VAR_COURSEID));
810 $criterion->set_source_table('badge_criteria', array('badgeid' => backup::VAR_PARENTID));
811
812 $parametersql = 'SELECT cp.*, c.criteriatype
813 FROM {badge_criteria_param} cp JOIN {badge_criteria} c
814 ON cp.critid = c.id
815 WHERE critid = :critid';
816 $parameterparams = array('critid' => backup::VAR_PARENTID);
817 $parameter->set_source_sql($parametersql, $parameterparams);
818
bd0d2bdc
YB
819 $manual_award->set_source_table('badge_manual_award', array('badgeid' => backup::VAR_PARENTID));
820
2832093f
YB
821 // Define id annotations.
822
823 $badge->annotate_ids('user', 'usercreated');
824 $badge->annotate_ids('user', 'usermodified');
825 $criterion->annotate_ids('badge', 'badgeid');
826 $parameter->annotate_ids('criterion', 'critid');
827 $badge->annotate_files('badges', 'badgeimage', 'id');
bd0d2bdc
YB
828 $manual_award->annotate_ids('badge', 'badgeid');
829 $manual_award->annotate_ids('user', 'recipientid');
830 $manual_award->annotate_ids('user', 'issuerid');
831 $manual_award->annotate_ids('role', 'issuerrole');
2832093f
YB
832
833 // Return the root element ($badges).
834 return $badges;
835 }
836}
837
8331a159
AA
838/**
839 * structure step in charge of constructing the calender.xml file for all the events found
840 * in a given context
841 */
842class backup_calendarevents_structure_step extends backup_structure_step {
843
844 protected function define_structure() {
845
846 // Define each element separated
847
848 $events = new backup_nested_element('events');
849
850 $event = new backup_nested_element('event', array('id'), array(
851 'name', 'description', 'format', 'courseid', 'groupid', 'userid',
852 'repeatid', 'modulename', 'instance', 'eventtype', 'timestart',
853 'timeduration', 'visible', 'uuid', 'sequence', 'timemodified'));
854
855 // Build the tree
856 $events->add_child($event);
857
858 // Define sources
859 if ($this->name == 'course_calendar') {
860 $calendar_items_sql ="SELECT * FROM {event}
861 WHERE courseid = :courseid
862 AND (eventtype = 'course' OR eventtype = 'group')";
863 $calendar_items_params = array('courseid'=>backup::VAR_COURSEID);
864 $event->set_source_sql($calendar_items_sql, $calendar_items_params);
865 } else {
866 $event->set_source_table('event', array('courseid' => backup::VAR_COURSEID, 'instance' => backup::VAR_ACTIVITYID, 'modulename' => backup::VAR_MODNAME));
867 }
868
869 // Define id annotations
870
871 $event->annotate_ids('user', 'userid');
872 $event->annotate_ids('group', 'groupid');
873 $event->annotate_files('calendar', 'event_description', 'id');
874
875 // Return the root element (events)
876 return $events;
877 }
6565d55a
EL
878}
879
315f6d8e
AD
880/**
881 * structure step in charge of constructing the gradebook.xml file for all the gradebook config in the course
882 * NOTE: the backup of the grade items themselves is handled by backup_activity_grades_structure_step
883 */
884class backup_gradebook_structure_step extends backup_structure_step {
885
d39595cc
EL
886 /**
887 * We need to decide conditionally, based on dynamic information
888 * about the execution of this step. Only will be executed if all
889 * the module gradeitems have been already included in backup
890 */
891 protected function execute_condition() {
03c39daa
DM
892 $courseid = $this->get_courseid();
893 if ($courseid == SITEID) {
894 return false;
895 }
896
897 return backup_plan_dbops::require_gradebook_backup($courseid, $this->get_backupid());
d39595cc
EL
898 }
899
315f6d8e 900 protected function define_structure() {
cd4061ec 901 global $CFG, $DB;
315f6d8e
AD
902
903 // are we including user info?
904 $userinfo = $this->get_setting_value('users');
905
906 $gradebook = new backup_nested_element('gradebook');
907
908 //grade_letters are done in backup_activity_grades_structure_step()
909
910 //calculated grade items
911 $grade_items = new backup_nested_element('grade_items');
912 $grade_item = new backup_nested_element('grade_item', array('id'), array(
913 'categoryid', 'itemname', 'itemtype', 'itemmodule',
914 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
915 'calculation', 'gradetype', 'grademax', 'grademin',
916 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
b4087c31
FM
917 'plusfactor', 'aggregationcoef', 'aggregationcoef2', 'weightoverride',
918 'sortorder', 'display', 'decimals', 'hidden', 'locked', 'locktime',
315f6d8e
AD
919 'needsupdate', 'timecreated', 'timemodified'));
920
921 $grade_grades = new backup_nested_element('grade_grades');
922 $grade_grade = new backup_nested_element('grade_grade', array('id'), array(
923 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
924 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
925 'locked', 'locktime', 'exported', 'overridden',
926 'excluded', 'feedback', 'feedbackformat', 'information',
8836ef8a
DW
927 'informationformat', 'timecreated', 'timemodified',
928 'aggregationstatus', 'aggregationweight'));
315f6d8e
AD
929
930 //grade_categories
931 $grade_categories = new backup_nested_element('grade_categories');
0067d939 932 $grade_category = new backup_nested_element('grade_category', array('id'), array(
158e9e2a 933 //'courseid',
9a20df96 934 'parent', 'depth', 'path', 'fullname', 'aggregation', 'keephigh',
47d6e6a7 935 'droplow', 'aggregateonlygraded', 'aggregateoutcomes',
90c29857 936 'timecreated', 'timemodified', 'hidden'));
315f6d8e
AD
937
938 $letters = new backup_nested_element('grade_letters');
939 $letter = new backup_nested_element('grade_letter', 'id', array(
e101180d 940 'lowerboundary', 'letter'));
315f6d8e 941
b8040c83
AD
942 $grade_settings = new backup_nested_element('grade_settings');
943 $grade_setting = new backup_nested_element('grade_setting', 'id', array(
944 'name', 'value'));
945
315f6d8e
AD
946
947 // Build the tree
0067d939
AD
948 $gradebook->add_child($grade_categories);
949 $grade_categories->add_child($grade_category);
315f6d8e
AD
950
951 $gradebook->add_child($grade_items);
952 $grade_items->add_child($grade_item);
953 $grade_item->add_child($grade_grades);
954 $grade_grades->add_child($grade_grade);
955
315f6d8e
AD
956 $gradebook->add_child($letters);
957 $letters->add_child($letter);
958
b8040c83
AD
959 $gradebook->add_child($grade_settings);
960 $grade_settings->add_child($grade_setting);
961
deb3d5ed
MG
962 // Add attribute with gradebook calculation freeze date if needed.
963 $gradebookcalculationfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->get_courseid());
964 if ($gradebookcalculationfreeze) {
965 $gradebook->add_attributes(array('calculations_freeze'));
966 $gradebook->get_attribute('calculations_freeze')->set_value($gradebookcalculationfreeze);
967 }
968
315f6d8e
AD
969 // Define sources
970
be739b71 971 //Include manual, category and the course grade item
58328ce8 972 $grade_items_sql ="SELECT * FROM {grade_items}
be739b71
AD
973 WHERE courseid = :courseid
974 AND (itemtype='manual' OR itemtype='course' OR itemtype='category')";
3f92c2fc 975 $grade_items_params = array('courseid'=>backup::VAR_COURSEID);
be739b71 976 $grade_item->set_source_sql($grade_items_sql, $grade_items_params);
315f6d8e
AD
977
978 if ($userinfo) {
979 $grade_grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
980 }
981
982 $grade_category_sql = "SELECT gc.*, gi.sortorder
983 FROM {grade_categories} gc
984 JOIN {grade_items} gi ON (gi.iteminstance = gc.id)
985 WHERE gc.courseid = :courseid
986 AND (gi.itemtype='course' OR gi.itemtype='category')
987 ORDER BY gc.parent ASC";//need parent categories before their children
988 $grade_category_params = array('courseid'=>backup::VAR_COURSEID);
989 $grade_category->set_source_sql($grade_category_sql, $grade_category_params);
990
991 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
992
cd4061ec
FM
993 // Set the grade settings source, forcing the inclusion of minmaxtouse if not present.
994 $settings = array();
995 $rs = $DB->get_recordset('grade_settings', array('courseid' => $this->get_courseid()));
996 foreach ($rs as $record) {
997 $settings[$record->name] = $record;
998 }
999 $rs->close();
1000 if (!isset($settings['minmaxtouse'])) {
1001 $settings['minmaxtouse'] = (object) array('name' => 'minmaxtouse', 'value' => $CFG->grade_minmaxtouse);
1002 }
1003 $grade_setting->set_source_array($settings);
1004
b8040c83 1005
76cfb124 1006 // Annotations (both as final as far as they are going to be exported in next steps)
315f6d8e 1007 $grade_item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
76cfb124 1008 $grade_item->annotate_ids('outcomefinal', 'outcomeid');
315f6d8e 1009
9a20df96
AD
1010 //just in case there are any users not already annotated by the activities
1011 $grade_grade->annotate_ids('userfinal', 'userid');
1012
315f6d8e
AD
1013 // Return the root element
1014 return $gradebook;
1015 }
1016}
1017
167eb033
FM
1018/**
1019 * Step in charge of constructing the grade_history.xml file containing the grade histories.
1020 */
1021class backup_grade_history_structure_step extends backup_structure_step {
1022
1023 /**
1024 * Limit the execution.
1025 *
1026 * This applies the same logic than the one applied to {@link backup_gradebook_structure_step},
1027 * because we do not want to save the history of items which are not backed up. At least for now.
1028 */
1029 protected function execute_condition() {
03c39daa
DM
1030 $courseid = $this->get_courseid();
1031 if ($courseid == SITEID) {
1032 return false;
1033 }
1034
1035 return backup_plan_dbops::require_gradebook_backup($courseid, $this->get_backupid());
167eb033
FM
1036 }
1037
1038 protected function define_structure() {
1039
1040 // Settings to use.
1041 $userinfo = $this->get_setting_value('users');
1042 $history = $this->get_setting_value('grade_histories');
1043
1044 // Create the nested elements.
1045 $bookhistory = new backup_nested_element('grade_history');
1046 $grades = new backup_nested_element('grade_grades');
1047 $grade = new backup_nested_element('grade_grade', array('id'), array(
1048 'action', 'oldid', 'source', 'loggeduser', 'itemid', 'userid',
1049 'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid',
1050 'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden',
1051 'excluded', 'feedback', 'feedbackformat', 'information',
1052 'informationformat', 'timemodified'));
1053
1054 // Build the tree.
1055 $bookhistory->add_child($grades);
1056 $grades->add_child($grade);
1057
1058 // This only happens if we are including user info and history.
1059 if ($userinfo && $history) {
1060 // Only keep the history of grades related to items which have been backed up, The query is
1061 // similar (but not identical) to the one used in backup_gradebook_structure_step::define_structure().
1062 $gradesql = "SELECT ggh.*
1063 FROM {grade_grades_history} ggh
1064 JOIN {grade_items} gi ON ggh.itemid = gi.id
1065 WHERE gi.courseid = :courseid
1066 AND (gi.itemtype = 'manual' OR gi.itemtype = 'course' OR gi.itemtype = 'category')";
1067 $grade->set_source_sql($gradesql, array('courseid' => backup::VAR_COURSEID));
1068 }
1069
1070 // Annotations. (Final annotations as this step is part of the final task).
1071 $grade->annotate_ids('scalefinal', 'rawscaleid');
1072 $grade->annotate_ids('userfinal', 'loggeduser');
1073 $grade->annotate_ids('userfinal', 'userid');
1074 $grade->annotate_ids('userfinal', 'usermodified');
1075
1076 // Return the root element.
1077 return $bookhistory;
1078 }
1079
1080}
1081
77547b46
EL
1082/**
1083 * structure step in charge if constructing the completion.xml file for all the users completion
1084 * information in a given activity
1085 */
1086class backup_userscompletion_structure_step extends backup_structure_step {
1087
03c39daa
DM
1088 /**
1089 * Skip completion on the front page.
1090 * @return bool
1091 */
1092 protected function execute_condition() {
1093 return ($this->get_courseid() != SITEID);
1094 }
1095
77547b46
EL
1096 protected function define_structure() {
1097
1098 // Define each element separated
1099
1100 $completions = new backup_nested_element('completions');
1101
1102 $completion = new backup_nested_element('completion', array('id'), array(
1103 'userid', 'completionstate', 'viewed', 'timemodified'));
1104
1105 // Build the tree
1106
1107 $completions->add_child($completion);
1108
1109 // Define sources
1110
1111 $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
1112
1113 // Define id annotations
1114
1115 $completion->annotate_ids('user', 'userid');
1116
1117 // Return the root element (completions)
1118 return $completions;
1119 }
1120}
1121
1122/**
1123 * structure step in charge of constructing the main groups.xml file for all the groups and
1124 * groupings information already annotated
1125 */
1126class backup_groups_structure_step extends backup_structure_step {
1127
1128 protected function define_structure() {
1129
868b086c
MS
1130 // To know if we are including users.
1131 $userinfo = $this->get_setting_value('users');
1132 // To know if we are including groups and groupings.
1133 $groupinfo = $this->get_setting_value('groups');
1134
77547b46
EL
1135 // Define each element separated
1136
1137 $groups = new backup_nested_element('groups');
1138
1139 $group = new backup_nested_element('group', array('id'), array(
74b714df 1140 'name', 'idnumber', 'description', 'descriptionformat', 'enrolmentkey',
77547b46
EL
1141 'picture', 'hidepicture', 'timecreated', 'timemodified'));
1142
1143 $members = new backup_nested_element('group_members');
1144
1145 $member = new backup_nested_element('group_member', array('id'), array(
1d1917ae 1146 'userid', 'timeadded', 'component', 'itemid'));
77547b46
EL
1147
1148 $groupings = new backup_nested_element('groupings');
1149
1150 $grouping = new backup_nested_element('grouping', 'id', array(
74b714df 1151 'name', 'idnumber', 'description', 'descriptionformat', 'configdata',
77547b46
EL
1152 'timecreated', 'timemodified'));
1153
1154 $groupinggroups = new backup_nested_element('grouping_groups');
1155
1156 $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
1157 'groupid', 'timeadded'));
1158
1159 // Build the tree
1160
1161 $groups->add_child($group);
1162 $groups->add_child($groupings);
1163
1164 $group->add_child($members);
1165 $members->add_child($member);
1166
1167 $groupings->add_child($grouping);
1168 $grouping->add_child($groupinggroups);
1169 $groupinggroups->add_child($groupinggroup);
1170
1171 // Define sources
1172
868b086c
MS
1173 // This only happens if we are including groups/groupings.
1174 if ($groupinfo) {
1175 $group->set_source_sql("
1176 SELECT g.*
1177 FROM {groups} g
1178 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
1179 WHERE bi.backupid = ?
1180 AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
1181
1182 $grouping->set_source_sql("
1183 SELECT g.*
1184 FROM {groupings} g
1185 JOIN {backup_ids_temp} bi ON g.id = bi.itemid
1186 WHERE bi.backupid = ?
1187 AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
1188 $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
1189
d7891c83 1190 // This only happens if we are including users.
868b086c
MS
1191 if ($userinfo) {
1192 $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
1193 }
77547b46
EL
1194 }
1195
77547b46
EL
1196 // Define id annotations (as final)
1197
1198 $member->annotate_ids('userfinal', 'userid');
1199
1200 // Define file annotations
1201
64f93798 1202 $group->annotate_files('group', 'description', 'id');
78d47b30 1203 $group->annotate_files('group', 'icon', 'id');
5cc70f32 1204 $grouping->annotate_files('grouping', 'description', 'id');
77547b46
EL
1205
1206 // Return the root element (groups)
1207 return $groups;
1208 }
1209}
1210
1211/**
1212 * structure step in charge of constructing the main users.xml file for all the users already
1213 * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
1214 * overrides.
1215 */
1216class backup_users_structure_step extends backup_structure_step {
1217
1218 protected function define_structure() {
1219 global $CFG;
1220
1221 // To know if we are anonymizing users
1222 $anonymize = $this->get_setting_value('anonymize');
1223 // To know if we are including role assignments
1224 $roleassignments = $this->get_setting_value('role_assignments');
1225
868b086c 1226 // Define each element separate.
77547b46
EL
1227
1228 $users = new backup_nested_element('users');
1229
1230 // Create the array of user fields by hand, as far as we have various bits to control
1231 // anonymize option, password backup, mnethostid...
1232
1233 // First, the fields not needing anonymization nor special handling
1234 $normalfields = array(
1235 'confirmed', 'policyagreed', 'deleted',
1236 'lang', 'theme', 'timezone', 'firstaccess',
482aac65 1237 'lastaccess', 'lastlogin', 'currentlogin',
3d27180e 1238 'mailformat', 'maildigest', 'maildisplay',
a80b5a0c 1239 'autosubscribe', 'trackforums', 'timecreated',
d11f12f6 1240 'timemodified', 'trustbitmask');
77547b46
EL
1241
1242 // Then, the fields potentially needing anonymization
1243 $anonfields = array(
a327f25e 1244 'username', 'idnumber', 'email', 'icq', 'skype',
482aac65
EL
1245 'yahoo', 'aim', 'msn', 'phone1',
1246 'phone2', 'institution', 'department', 'address',
1247 'city', 'country', 'lastip', 'picture',
c44d4aee 1248 'url', 'description', 'descriptionformat', 'imagealt', 'auth');
a327f25e 1249 $anonfields = array_merge($anonfields, get_all_user_name_fields());
77547b46
EL
1250
1251 // Add anonymized fields to $userfields with custom final element
1252 foreach ($anonfields as $field) {
1253 if ($anonymize) {
1254 $userfields[] = new anonymizer_final_element($field);
1255 } else {
1256 $userfields[] = $field; // No anonymization, normally added
1257 }
1258 }
1259
1260 // mnethosturl requires special handling (custom final element)
1261 $userfields[] = new mnethosturl_final_element('mnethosturl');
1262
1263 // password added conditionally
1264 if (!empty($CFG->includeuserpasswordsinbackup)) {
1265 $userfields[] = 'password';
1266 }
1267
1268 // Merge all the fields
1269 $userfields = array_merge($userfields, $normalfields);
1270
1271 $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
1272
1273 $customfields = new backup_nested_element('custom_fields');
1274
1275 $customfield = new backup_nested_element('custom_field', array('id'), array(
1276 'field_name', 'field_type', 'field_data'));
1277
1278 $tags = new backup_nested_element('tags');
1279
1280 $tag = new backup_nested_element('tag', array('id'), array(
1281 'name', 'rawname'));
1282
1283 $preferences = new backup_nested_element('preferences');
1284
1285 $preference = new backup_nested_element('preference', array('id'), array(
1286 'name', 'value'));
1287
1288 $roles = new backup_nested_element('roles');
1289
1290 $overrides = new backup_nested_element('role_overrides');
1291
1292 $override = new backup_nested_element('override', array('id'), array(
1293 'roleid', 'capability', 'permission', 'timemodified',
1294 'modifierid'));
1295
1296 $assignments = new backup_nested_element('role_assignments');
1297
1298 $assignment = new backup_nested_element('assignment', array('id'), array(
df997f84 1299 'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
77547b46
EL
1300 'sortorder'));
1301
1302 // Build the tree
1303
1304 $users->add_child($user);
1305
1306 $user->add_child($customfields);
1307 $customfields->add_child($customfield);
1308
1309 $user->add_child($tags);
1310 $tags->add_child($tag);
1311
1312 $user->add_child($preferences);
1313 $preferences->add_child($preference);
1314
1315 $user->add_child($roles);
1316
1317 $roles->add_child($overrides);
1318 $roles->add_child($assignments);
1319
1320 $overrides->add_child($override);
1321 $assignments->add_child($assignment);
1322
1323 // Define sources
1324
1325 $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
1326 FROM {user} u
1327 JOIN {backup_ids_temp} bi ON bi.itemid = u.id
ef77af38 1328 LEFT JOIN {context} c ON c.instanceid = u.id AND c.contextlevel = ' . CONTEXT_USER . '
77547b46
EL
1329 LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
1330 WHERE bi.backupid = ?
ef77af38 1331 AND bi.itemname = ?', array(
c0bd6249 1332 backup_helper::is_sqlparam($this->get_backupid()),
ef77af38 1333 backup_helper::is_sqlparam('userfinal')));
77547b46
EL
1334
1335 // All the rest on information is only added if we arent
1336 // in an anonymized backup
1337 if (!$anonymize) {
1338 $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
1339 FROM {user_info_field} f
1340 JOIN {user_info_data} d ON d.fieldid = f.id
1341 WHERE d.userid = ?', array(backup::VAR_PARENTID));
1342
1343 $customfield->set_source_alias('shortname', 'field_name');
1344 $customfield->set_source_alias('datatype', 'field_type');
1345 $customfield->set_source_alias('data', 'field_data');
1346
1347 $tag->set_source_sql('SELECT t.id, t.name, t.rawname
1348 FROM {tag} t
1349 JOIN {tag_instance} ti ON ti.tagid = t.id
1350 WHERE ti.itemtype = ?
1351 AND ti.itemid = ?', array(
c0bd6249 1352 backup_helper::is_sqlparam('user'),
77547b46
EL
1353 backup::VAR_PARENTID));
1354
1355 $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
1356
1357 $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
1358
1359 // Assignments only added if specified
1360 if ($roleassignments) {
1361 $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
1362 }
1363
1364 // Define id annotations (as final)
1365 $override->annotate_ids('rolefinal', 'roleid');
1366 }
1367
1368 // Return root element (users)
1369 return $users;
1370 }
1371}
1372
1373/**
1374 * structure step in charge of constructing the block.xml file for one
39b5371c 1375 * given block (instance and positions). If the block has custom DB structure
77547b46
EL
1376 * that will go to a separate file (different step defined in block class)
1377 */
1378class backup_block_instance_structure_step extends backup_structure_step {
1379
1380 protected function define_structure() {
1381 global $DB;
1382
1383 // Define each element separated
1384
5f8354eb 1385 $block = new backup_nested_element('block', array('id', 'contextid', 'version'), array(
61243f3a
EL
1386 'blockname', 'parentcontextid', 'showinsubcontexts', 'pagetypepattern',
1387 'subpagepattern', 'defaultregion', 'defaultweight', 'configdata'));
77547b46 1388
2d7cd798
EL
1389 $positions = new backup_nested_element('block_positions');
1390
1391 $position = new backup_nested_element('block_position', array('id'), array(
77547b46
EL
1392 'contextid', 'pagetype', 'subpage', 'visible',
1393 'region', 'weight'));
1394
1395 // Build the tree
1396
1397 $block->add_child($positions);
2d7cd798 1398 $positions->add_child($position);
77547b46
EL
1399
1400 // Transform configdata information if needed (process links and friends)
1401 $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
1402 if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
1403 $configdata = (array)unserialize(base64_decode($blockrec->configdata));
1404 foreach ($configdata as $attribute => $value) {
1405 if (in_array($attribute, $attrstotransform)) {
1406 $configdata[$attribute] = $this->contenttransformer->process($value);
1407 }
1408 }
1409 $blockrec->configdata = base64_encode(serialize((object)$configdata));
1410 }
5f8354eb 1411 $blockrec->contextid = $this->task->get_contextid();
77547b46 1412 // Get the version of the block
bde002b8 1413 $blockrec->version = get_config('block_'.$this->task->get_blockname(), 'version');
77547b46
EL
1414
1415 // Define sources
1416
1417 $block->set_source_array(array($blockrec));
1418
2d7cd798 1419 $position->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
77547b46 1420
4a15bb76
EL
1421 // File anotations (for fileareas specified on each block)
1422 foreach ($this->task->get_fileareas() as $filearea) {
1423 $block->annotate_files('block_' . $this->task->get_blockname(), $filearea, null);
1424 }
1425
77547b46
EL
1426 // Return the root element (block)
1427 return $block;
1428 }
1429}
1430
0f66aced
EL
1431/**
1432 * structure step in charge of constructing the logs.xml file for all the log records found
1433 * in course. Note that we are sending to backup ALL the log records having cmid = 0. That
1434 * includes some records that won't be restoreable (like 'upload', 'calendar'...) but we do
1435 * that just in case they become restored some day in the future
1436 */
1437class backup_course_logs_structure_step extends backup_structure_step {
1438
1439 protected function define_structure() {
1440
1441 // Define each element separated
1442
1443 $logs = new backup_nested_element('logs');
1444
1445 $log = new backup_nested_element('log', array('id'), array(
1446 'time', 'userid', 'ip', 'module',
1447 'action', 'url', 'info'));
1448
1449 // Build the tree
1450
1451 $logs->add_child($log);
1452
1453 // Define sources (all the records belonging to the course, having cmid = 0)
1454
1455 $log->set_source_table('log', array('course' => backup::VAR_COURSEID, 'cmid' => backup_helper::is_sqlparam(0)));
1456
1457 // Annotations
1458 // NOTE: We don't annotate users from logs as far as they MUST be
1459 // always annotated by the course (enrol, ras... whatever)
1460
1461 // Return the root element (logs)
1462
1463 return $logs;
1464 }
1465}
1466
77547b46
EL
1467/**
1468 * structure step in charge of constructing the logs.xml file for all the log records found
1469 * in activity
1470 */
1471class backup_activity_logs_structure_step extends backup_structure_step {
1472
1473 protected function define_structure() {
1474
1475 // Define each element separated
1476
1477 $logs = new backup_nested_element('logs');
1478
1479 $log = new backup_nested_element('log', array('id'), array(
1480 'time', 'userid', 'ip', 'module',
1481 'action', 'url', 'info'));
1482
1483 // Build the tree
1484
1485 $logs->add_child($log);
1486
1487 // Define sources
1488
1489 $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
1490
1491 // Annotations
1492 // NOTE: We don't annotate users from logs as far as they MUST be
0f66aced 1493 // always annotated by the activity (true participants).
77547b46
EL
1494
1495 // Return the root element (logs)
1496
1497 return $logs;
1498 }
1499}
1500
73c2a354
EL
1501/**
1502 * Structure step in charge of constructing the logstores.xml file for the course logs.
1503 *
1504 * This backup step will backup the logs for all the enabled logstore subplugins supporting
1505 * it, for logs belonging to the course level.
1506 */
1507class backup_course_logstores_structure_step extends backup_structure_step {
1508
1509 protected function define_structure() {
1510
1511 // Define the structure of logstores container.
1512 $logstores = new backup_nested_element('logstores');
1513 $logstore = new backup_nested_element('logstore');
1514 $logstores->add_child($logstore);
1515
1516 // Add the tool_log logstore subplugins information to the logstore element.
1517 $this->add_subplugin_structure('logstore', $logstore, true, 'tool', 'log');
1518
1519 return $logstores;
1520 }
1521}
1522
1523/**
1524 * Structure step in charge of constructing the logstores.xml file for the activity logs.
1525 *
1526 * Note: Activity structure is completely equivalent to the course one, so just extend it.
1527 */
1528class backup_activity_logstores_structure_step extends backup_course_logstores_structure_step {
1529}
1530
77547b46
EL
1531/**
1532 * structure in charge of constructing the inforef.xml file for all the items we want
1533 * to have referenced there (users, roles, files...)
1534 */
1535class backup_inforef_structure_step extends backup_structure_step {
1536
1537 protected function define_structure() {
1538
482aac65
EL
1539 // Items we want to include in the inforef file.
1540 $items = backup_helper::get_inforef_itemnames();
77547b46
EL
1541
1542 // Build the tree
1543
1544 $inforef = new backup_nested_element('inforef');
1545
1546 // For each item, conditionally, if there are already records, build element
1547 foreach ($items as $itemname) {
1548 if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
1549 $elementroot = new backup_nested_element($itemname . 'ref');
482aac65 1550 $element = new backup_nested_element($itemname, array(), array('id'));
77547b46
EL
1551 $inforef->add_child($elementroot);
1552 $elementroot->add_child($element);
1553 $element->set_source_sql("
1554 SELECT itemid AS id
1555 FROM {backup_ids_temp}
1556 WHERE backupid = ?
1557 AND itemname = ?",
c0bd6249 1558 array(backup::VAR_BACKUPID, backup_helper::is_sqlparam($itemname)));
77547b46
EL
1559 }
1560 }
1561
1562 // We don't annotate anything there, but rely in the next step
1563 // (move_inforef_annotations_to_final) that will change all the
1564 // already saved 'inforref' entries to their 'final' annotations.
1565 return $inforef;
1566 }
1567}
1568
1569/**
1570 * This step will get all the annotations already processed to inforef.xml file and
1571 * transform them into 'final' annotations.
1572 */
1573class move_inforef_annotations_to_final extends backup_execution_step {
1574
1575 protected function define_execution() {
1576
482aac65
EL
1577 // Items we want to include in the inforef file
1578 $items = backup_helper::get_inforef_itemnames();
2a70b70c 1579 $progress = $this->task->get_progress();
1580 $progress->start_progress($this->get_name(), count($items));
1581 $done = 1;
77547b46
EL
1582 foreach ($items as $itemname) {
1583 // Delegate to dbops
2a70b70c 1584 backup_structure_dbops::move_annotations_to_final($this->get_backupid(),
1585 $itemname, $progress);
1586 $progress->progress($done++);
77547b46 1587 }
2a70b70c 1588 $progress->end_progress();
77547b46
EL
1589 }
1590}
1591
1592/**
1593 * structure in charge of constructing the files.xml file with all the
1594 * annotated (final) files along the process. At, the same time, and
1595 * using one specialised nested_element, will copy them form moodle storage
1596 * to backup storage
1597 */
1598class backup_final_files_structure_step extends backup_structure_step {
1599
1600 protected function define_structure() {
1601
1602 // Define elements
1603
1604 $files = new backup_nested_element('files');
1605
1606 $file = new file_nested_element('file', array('id'), array(
64f93798 1607 'contenthash', 'contextid', 'component', 'filearea', 'itemid',
77547b46
EL
1608 'filepath', 'filename', 'userid', 'filesize',
1609 'mimetype', 'status', 'timecreated', 'timemodified',
79b810fd
DM
1610 'source', 'author', 'license', 'sortorder',
1611 'repositorytype', 'repositoryid', 'reference'));
77547b46
EL
1612
1613 // Build the tree
1614
1615 $files->add_child($file);
1616
1617 // Define sources
1618
79b810fd 1619 $file->set_source_sql("SELECT f.*, r.type AS repositorytype, fr.repositoryid, fr.reference
77547b46 1620 FROM {files} f
79b810fd
DM
1621 LEFT JOIN {files_reference} fr ON fr.id = f.referencefileid
1622 LEFT JOIN {repository_instances} ri ON ri.id = fr.repositoryid
1623 LEFT JOIN {repository} r ON r.id = ri.typeid
1624 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
77547b46
EL
1625 WHERE bi.backupid = ?
1626 AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
1627
1628 return $files;
1629 }
1630}
1631
1632/**
1633 * Structure step in charge of creating the main moodle_backup.xml file
1634 * where all the information related to the backup, settings, license and
1635 * other information needed on restore is added*/
1636class backup_main_structure_step extends backup_structure_step {
1637
1638 protected function define_structure() {
1639
1640 global $CFG;
1641
1642 $info = array();
1643
1644 $info['name'] = $this->get_setting_value('filename');
1645 $info['moodle_version'] = $CFG->version;
1646 $info['moodle_release'] = $CFG->release;
1647 $info['backup_version'] = $CFG->backup_version;
1648 $info['backup_release'] = $CFG->backup_release;
1649 $info['backup_date'] = time();
1650 $info['backup_uniqueid']= $this->get_backupid();
c3ea499d 1651 $info['mnet_remoteusers']=backup_controller_dbops::backup_includes_mnet_remote_users($this->get_backupid());
b1850c12 1652 $info['include_files'] = backup_controller_dbops::backup_includes_files($this->get_backupid());
67233725
DC
1653 $info['include_file_references_to_external_content'] =
1654 backup_controller_dbops::backup_includes_file_references($this->get_backupid());
77547b46 1655 $info['original_wwwroot']=$CFG->wwwroot;
482aac65 1656 $info['original_site_identifier_hash'] = md5(get_site_identifier());
77547b46 1657 $info['original_course_id'] = $this->get_courseid();
560811a9 1658 $originalcourseinfo = backup_controller_dbops::backup_get_original_course_info($this->get_courseid());
03c39daa 1659 $info['original_course_format'] = $originalcourseinfo->format;
560811a9
EL
1660 $info['original_course_fullname'] = $originalcourseinfo->fullname;
1661 $info['original_course_shortname'] = $originalcourseinfo->shortname;
1662 $info['original_course_startdate'] = $originalcourseinfo->startdate;
a689cd1d
AG
1663 $info['original_course_contextid'] = context_course::instance($this->get_courseid())->id;
1664 $info['original_system_contextid'] = context_system::instance()->id;
77547b46
EL
1665
1666 // Get more information from controller
e555a443 1667 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information(
1668 $this->get_backupid(), $this->get_task()->get_progress());
77547b46
EL
1669
1670 // Define elements
1671
1672 $moodle_backup = new backup_nested_element('moodle_backup');
1673
1674 $information = new backup_nested_element('information', null, array(
1675 'name', 'moodle_version', 'moodle_release', 'backup_version',
b1850c12 1676 'backup_release', 'backup_date', 'mnet_remoteusers', 'include_files', 'include_file_references_to_external_content', 'original_wwwroot',
03c39daa 1677 'original_site_identifier_hash', 'original_course_id', 'original_course_format',
560811a9
EL
1678 'original_course_fullname', 'original_course_shortname', 'original_course_startdate',
1679 'original_course_contextid', 'original_system_contextid'));
77547b46
EL
1680
1681 $details = new backup_nested_element('details');
1682
1683 $detail = new backup_nested_element('detail', array('backup_id'), array(
1684 'type', 'format', 'interactive', 'mode',
1685 'execution', 'executiontime'));
1686
1687 $contents = new backup_nested_element('contents');
1688
1689 $activities = new backup_nested_element('activities');
1690
1691 $activity = new backup_nested_element('activity', null, array(
1692 'moduleid', 'sectionid', 'modulename', 'title',
1693 'directory'));
1694
1695 $sections = new backup_nested_element('sections');
1696
1697 $section = new backup_nested_element('section', null, array(
1698 'sectionid', 'title', 'directory'));
1699
1700 $course = new backup_nested_element('course', null, array(
1701 'courseid', 'title', 'directory'));
1702
1703 $settings = new backup_nested_element('settings');
1704
1705 $setting = new backup_nested_element('setting', null, array(
d12fd69b 1706 'level', 'section', 'activity', 'name', 'value'));
77547b46
EL
1707
1708 // Build the tree
1709
1710 $moodle_backup->add_child($information);
1711
1712 $information->add_child($details);
1713 $details->add_child($detail);
1714
1715 $information->add_child($contents);
1716 if (!empty($cinfo['activities'])) {
1717 $contents->add_child($activities);
1718 $activities->add_child($activity);
1719 }
1720 if (!empty($cinfo['sections'])) {
1721 $contents->add_child($sections);
1722 $sections->add_child($section);
1723 }
1724 if (!empty($cinfo['course'])) {
1725 $contents->add_child($course);
1726 }
1727
1728 $information->add_child($settings);
1729 $settings->add_child($setting);
1730
1731
1732 // Set the sources
1733
1734 $information->set_source_array(array((object)$info));
1735
1736 $detail->set_source_array($dinfo);
1737
1738 $activity->set_source_array($cinfo['activities']);
1739
1740 $section->set_source_array($cinfo['sections']);
1741
1742 $course->set_source_array($cinfo['course']);
1743
1744 $setting->set_source_array($sinfo);
1745
1746 // Prepare some information to be sent to main moodle_backup.xml file
1747 return $moodle_backup;
1748 }
1749
1750}
1751
ce937f99 1752/**
12c80f79 1753 * Execution step that will generate the final zip (.mbz) file with all the contents
ce937f99 1754 */
2a70b70c 1755class backup_zip_contents extends backup_execution_step implements file_progress {
1756 /**
1757 * @var bool True if we have started tracking progress
1758 */
1759 protected $startedprogress;
ce937f99
EL
1760
1761 protected function define_execution() {
1762
1763 // Get basepath
1764 $basepath = $this->get_basepath();
1765
1766 // Get the list of files in directory
1767 $filestemp = get_directory_list($basepath, '', false, true, true);
1768 $files = array();
1769 foreach ($filestemp as $file) { // Add zip paths and fs paths to all them
1770 $files[$file] = $basepath . '/' . $file;
1771 }
1772
1773 // Add the log file if exists
1774 $logfilepath = $basepath . '.log';
1775 if (file_exists($logfilepath)) {
1776 $files['moodle_backup.log'] = $logfilepath;
1777 }
1778
12c80f79
EL
1779 // Calculate the zip fullpath (in OS temp area it's always backup.mbz)
1780 $zipfile = $basepath . '/backup.mbz';
ce937f99
EL
1781
1782 // Get the zip packer
39e5102f 1783 $zippacker = get_file_packer('application/vnd.moodle.backup');
ce937f99 1784
94b084fb 1785 // Track overall progress for the 2 long-running steps (archive to
1786 // pathname, get backup information).
1787 $reporter = $this->task->get_progress();
1788 $reporter->start_progress('backup_zip_contents', 2);
1789
ce937f99 1790 // Zip files
b2e791d1 1791 $result = $zippacker->archive_to_pathname($files, $zipfile, true, $this);
2a70b70c 1792
94b084fb 1793 // If any sub-progress happened, end it.
1794 if ($this->startedprogress) {
1795 $this->task->get_progress()->end_progress();
1796 $this->startedprogress = false;
1797 } else {
1798 // No progress was reported, manually move it on to the next overall task.
1799 $reporter->progress(1);
1800 }
1801
b2e791d1
AA
1802 // Something went wrong.
1803 if ($result === false) {
1804 @unlink($zipfile);
1805 throw new backup_step_exception('error_zip_packing', '', 'An error was encountered while trying to generate backup zip');
1806 }
1807 // Read to make sure it is a valid backup. Refer MDL-37877 . Delete it, if found not to be valid.
1808 try {
94b084fb 1809 backup_general_helper::get_backup_information_from_mbz($zipfile, $this);
b2e791d1
AA
1810 } catch (backup_helper_exception $e) {
1811 @unlink($zipfile);
1812 throw new backup_step_exception('error_zip_packing', '', $e->debuginfo);
1813 }
1814
94b084fb 1815 // If any sub-progress happened, end it.
2a70b70c 1816 if ($this->startedprogress) {
1817 $this->task->get_progress()->end_progress();
94b084fb 1818 $this->startedprogress = false;
1819 } else {
1820 $reporter->progress(2);
2a70b70c 1821 }
94b084fb 1822 $reporter->end_progress();
ce937f99 1823 }
2a70b70c 1824
1825 /**
1826 * Implementation for file_progress interface to display unzip progress.
1827 *
1828 * @param int $progress Current progress
1829 * @param int $max Max value
1830 */
1831 public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
1832 $reporter = $this->task->get_progress();
1833
1834 // Start tracking progress if necessary.
1835 if (!$this->startedprogress) {
1836 $reporter->start_progress('extract_file_to_dir', ($max == file_progress::INDETERMINATE)
809fdb83 1837 ? \core\progress\base::INDETERMINATE : $max);
2a70b70c 1838 $this->startedprogress = true;
1839 }
1840
1841 // Pass progress through to whatever handles it.
1842 $reporter->progress(($progress == file_progress::INDETERMINATE)
809fdb83 1843 ? \core\progress\base::INDETERMINATE : $progress);
2a70b70c 1844 }
ce937f99
EL
1845}
1846
1847/**
1848 * This step will send the generated backup file to its final destination
1849 */
1850class backup_store_backup_file extends backup_execution_step {
1851
1852 protected function define_execution() {
1853
1854 // Get basepath
1855 $basepath = $this->get_basepath();
1856
12c80f79
EL
1857 // Calculate the zip fullpath (in OS temp area it's always backup.mbz)
1858 $zipfile = $basepath . '/backup.mbz';
ce937f99 1859
67233725 1860 $has_file_references = backup_controller_dbops::backup_includes_file_references($this->get_backupid());
ce937f99 1861 // Perform storage and return it (TODO: shouldn't be array but proper result object)
67233725 1862 return array(
e555a443 1863 'backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile,
1864 $this->task->get_progress()),
67233725
DC
1865 'include_file_references_to_external_content' => $has_file_references
1866 );
ce937f99
EL
1867 }
1868}
1869
1870
77547b46
EL
1871/**
1872 * This step will search for all the activity (not calculations, categories nor aggregations) grade items
1873 * and put them to the backup_ids tables, to be used later as base to backup them
1874 */
1875class backup_activity_grade_items_to_ids extends backup_execution_step {
1876
1877 protected function define_execution() {
1878
1879 // Fetch all activity grade items
1880 if ($items = grade_item::fetch_all(array(
1881 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
1882 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
1883 // Annotate them in backup_ids
1884 foreach ($items as $item) {
1885 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
1886 }
1887 }
1888 }
1889}
1890
f6199295
MP
1891
1892/**
1893 * This step allows enrol plugins to annotate custom fields.
1894 *
1895 * @package core_backup
1896 * @copyright 2014 University of Wisconsin
1897 * @author Matt Petro
1898 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1899 */
1900class backup_enrolments_execution_step extends backup_execution_step {
1901
1902 /**
1903 * Function that will contain all the code to be executed.
1904 */
1905 protected function define_execution() {
1906 global $DB;
1907
1908 $plugins = enrol_get_plugins(true);
1909 $enrols = $DB->get_records('enrol', array(
1910 'courseid' => $this->task->get_courseid()));
1911
1912 // Allow each enrol plugin to add annotations.
1913 foreach ($enrols as $enrol) {
1914 if (isset($plugins[$enrol->enrol])) {
1915 $plugins[$enrol->enrol]->backup_annotate_custom_fields($this, $enrol);
1916 }
1917 }
1918 }
1919
1920 /**
1921 * Annotate a single name/id pair.
1922 * This can be called from {@link enrol_plugin::backup_annotate_custom_fields()}.
1923 *
1924 * @param string $itemname
1925 * @param int $itemid
1926 */
1927 public function annotate_id($itemname, $itemid) {
1928 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), $itemname, $itemid);
1929 }
1930}
1931
f70676a7
EL
1932/**
1933 * This step will annotate all the groups and groupings belonging to the course
1934 */
1935class backup_annotate_course_groups_and_groupings extends backup_execution_step {
1936
1937 protected function define_execution() {
1938 global $DB;
1939
1940 // Get all the course groups
1941 if ($groups = $DB->get_records('groups', array(
1942 'courseid' => $this->task->get_courseid()))) {
1943 foreach ($groups as $group) {
1944 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->id);
1945 }
1946 }
1947
1948 // Get all the course groupings
1949 if ($groupings = $DB->get_records('groupings', array(
1950 'courseid' => $this->task->get_courseid()))) {
1951 foreach ($groupings as $grouping) {
1952 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grouping', $grouping->id);
1953 }
1954 }
1955 }
1956}
1957
77547b46
EL
1958/**
1959 * This step will annotate all the groups belonging to already annotated groupings
1960 */
1961class backup_annotate_groups_from_groupings extends backup_execution_step {
1962
1963 protected function define_execution() {
1964 global $DB;
1965
1966 // Fetch all the annotated groupings
1967 if ($groupings = $DB->get_records('backup_ids_temp', array(
1968 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
1969 foreach ($groupings as $grouping) {
1970 if ($groups = $DB->get_records('groupings_groups', array(
1971 'groupingid' => $grouping->itemid))) {
1972 foreach ($groups as $group) {
1973 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
1974 }
1975 }
1976 }
1977 }
1978 }
1979}
1980
1981/**
1982 * This step will annotate all the scales belonging to already annotated outcomes
1983 */
1984class backup_annotate_scales_from_outcomes extends backup_execution_step {
1985
1986 protected function define_execution() {
1987 global $DB;
1988
1989 // Fetch all the annotated outcomes
1990 if ($outcomes = $DB->get_records('backup_ids_temp', array(
1991 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
1992 foreach ($outcomes as $outcome) {
1993 if ($scale = $DB->get_record('grade_outcomes', array(
1994 'id' => $outcome->itemid))) {
1995 // Annotate as scalefinal because it's > 0
1996 backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
1997 }
1998 }
1999 }
2000 }
2001}
2002
767cb7f0
EL
2003/**
2004 * This step will generate all the file annotations for the already
2005 * annotated (final) question_categories. It calculates the different
2006 * contexts that are being backup and, annotates all the files
2007 * on every context belonging to the "question" component. As far as
2008 * we are always including *complete* question banks it is safe and
2009 * optimal to do that in this (one pass) way
2010 */
2011class backup_annotate_all_question_files extends backup_execution_step {
2012
2013 protected function define_execution() {
2014 global $DB;
2015
2016 // Get all the different contexts for the final question_categories
2017 // annotated along the whole backup
2018 $rs = $DB->get_recordset_sql("SELECT DISTINCT qc.contextid
2019 FROM {question_categories} qc
2020 JOIN {backup_ids_temp} bi ON bi.itemid = qc.id
2021 WHERE bi.backupid = ?
2022 AND bi.itemname = 'question_categoryfinal'", array($this->get_backupid()));
41941110
EL
2023 // To know about qtype specific components/fileareas
2024 $components = backup_qtype_plugin::get_components_and_fileareas();
2025 // Let's loop
767cb7f0 2026 foreach($rs as $record) {
ba145a9e
TH
2027 // Backup all the file areas the are managed by the core question component.
2028 // That is, by the question_type base class. In particular, we don't want
2029 // to include files belonging to responses here.
2030 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'questiontext', null);
2031 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'generalfeedback', null);
2032 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'answer', null);
2033 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'answerfeedback', null);
2034 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'hint', null);
2035 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'correctfeedback', null);
2036 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'partiallycorrectfeedback', null);
2037 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', 'incorrectfeedback', null);
2038
2039 // For files belonging to question types, we make the leap of faith that
2040 // all the files belonging to the question type are part of the question definition,
2041 // so we can just backup all the files in bulk, without specifying each
2042 // file area name separately.
41941110
EL
2043 foreach ($components as $component => $fileareas) {
2044 backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, $component, null, null);
2045 }
767cb7f0
EL
2046 }
2047 $rs->close();
2048 }
2049}
2050
2051/**
2052 * structure step in charge of constructing the questions.xml file for all the
2053 * question categories and questions required by the backup
2054 * and letters related to one activity
2055 */
2056class backup_questions_structure_step extends backup_structure_step {
2057
2058 protected function define_structure() {
2059
2060 // Define each element separated
2061
2062 $qcategories = new backup_nested_element('question_categories');
2063
2064 $qcategory = new backup_nested_element('question_category', array('id'), array(
2065 'name', 'contextid', 'contextlevel', 'contextinstanceid',
2066 'info', 'infoformat', 'stamp', 'parent',
2067 'sortorder'));
2068
2069 $questions = new backup_nested_element('questions');
2070
2071 $question = new backup_nested_element('question', array('id'), array(
2072 'parent', 'name', 'questiontext', 'questiontextformat',
f3ca24e4 2073 'generalfeedback', 'generalfeedbackformat', 'defaultmark', 'penalty',
767cb7f0
EL
2074 'qtype', 'length', 'stamp', 'version',
2075 'hidden', 'timecreated', 'timemodified', 'createdby', 'modifiedby'));
2076
2077 // attach qtype plugin structure to $question element, only one allowed
2078 $this->add_plugin_structure('qtype', $question, false);
2079
a66e8bb3
SC
2080 // attach local plugin stucture to $question element, multiple allowed
2081 $this->add_plugin_structure('local', $question, true);
2082
f3ca24e4
TH
2083 $qhints = new backup_nested_element('question_hints');
2084
2085 $qhint = new backup_nested_element('question_hint', array('id'), array(
2086 'hint', 'hintformat', 'shownumcorrect', 'clearwrong', 'options'));
2087
f2dc4d24
MT
2088 $tags = new backup_nested_element('tags');
2089
2090 $tag = new backup_nested_element('tag', array('id'), array('name', 'rawname'));
2091
767cb7f0
EL
2092 // Build the tree
2093
2094 $qcategories->add_child($qcategory);
2095 $qcategory->add_child($questions);
767cb7f0 2096 $questions->add_child($question);
f3ca24e4
TH
2097 $question->add_child($qhints);
2098 $qhints->add_child($qhint);
767cb7f0 2099
f2dc4d24
MT
2100 $question->add_child($tags);
2101 $tags->add_child($tag);
2102
767cb7f0
EL
2103 // Define the sources
2104
2105 $qcategory->set_source_sql("
2106 SELECT gc.*, contextlevel, instanceid AS contextinstanceid
2107 FROM {question_categories} gc
2108 JOIN {backup_ids_temp} bi ON bi.itemid = gc.id
2109 JOIN {context} co ON co.id = gc.contextid
2110 WHERE bi.backupid = ?
2111 AND bi.itemname = 'question_categoryfinal'", array(backup::VAR_BACKUPID));
2112
2113 $question->set_source_table('question', array('category' => backup::VAR_PARENTID));
2114
c749527b
TH
2115 $qhint->set_source_sql('
2116 SELECT *
2117 FROM {question_hints}
2118 WHERE questionid = :questionid
2119 ORDER BY id',
2120 array('questionid' => backup::VAR_PARENTID));
f3ca24e4 2121
f2dc4d24
MT
2122 $tag->set_source_sql("SELECT t.id, t.name, t.rawname
2123 FROM {tag} t
2124 JOIN {tag_instance} ti ON ti.tagid = t.id
2125 WHERE ti.itemid = ?
2126 AND ti.itemtype = 'question'", array(backup::VAR_PARENTID));
2127
767cb7f0 2128 // don't need to annotate ids nor files
41941110 2129 // (already done by {@link backup_annotate_all_question_files}
767cb7f0
EL
2130
2131 return $qcategories;
2132 }
2133}
2134
2135
2136
77547b46 2137/**
76cfb124 2138 * This step will generate all the file annotations for the already
39b5371c 2139 * annotated (final) users. Need to do this here because each user
77547b46
EL
2140 * has its own context and structure tasks only are able to handle
2141 * one context. Also, this step will guarantee that every user has
2142 * its context created (req for other steps)
2143 */
2144class backup_annotate_all_user_files extends backup_execution_step {
2145
2146 protected function define_execution() {
2147 global $DB;
2148
2149 // List of fileareas we are going to annotate
76cfb124
EL
2150 $fileareas = array('profile', 'icon');
2151
77547b46
EL
2152 // Fetch all annotated (final) users
2153 $rs = $DB->get_recordset('backup_ids_temp', array(
2154 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
2a70b70c 2155 $progress = $this->task->get_progress();
2156 $progress->start_progress($this->get_name());
77547b46
EL
2157 foreach ($rs as $record) {
2158 $userid = $record->itemid;
92892849 2159 $userctx = context_user::instance($userid, IGNORE_MISSING);
ef77af38
EL
2160 if (!$userctx) {
2161 continue; // User has not context, sure it's a deleted user, so cannot have files
2162 }
77547b46
EL
2163 // Proceed with every user filearea
2164 foreach ($fileareas as $filearea) {
78d47b30 2165 // We don't need to specify itemid ($userid - 5th param) as far as by
77547b46 2166 // context we can get all the associated files. See MDL-22092
ef77af38 2167 backup_structure_dbops::annotate_files($this->get_backupid(), $userctx->id, 'user', $filearea, null);
2a70b70c 2168 $progress->progress();
77547b46
EL
2169 }
2170 }
2a70b70c 2171 $progress->end_progress();
77547b46
EL
2172 $rs->close();
2173 }
2174}
2175
9b553d47
DM
2176
2177/**
2178 * Defines the backup step for advanced grading methods attached to the activity module
2179 */
2180class backup_activity_grading_structure_step extends backup_structure_step {
2181
2182 /**
2183 * Include the grading.xml only if the module supports advanced grading
2184 */
2185 protected function execute_condition() {
03c39daa
DM
2186
2187 // No grades on the front page.
2188 if ($this->get_courseid() == SITEID) {
2189 return false;
2190 }
2191
9b553d47
DM
2192 return plugin_supports('mod', $this->get_task()->get_modulename(), FEATURE_ADVANCED_GRADING, false);
2193 }
2194
2195 /**
2196 * Declares the gradable areas structures and data sources
2197 */
2198 protected function define_structure() {
2199
2200 // To know if we are including userinfo
2201 $userinfo = $this->get_setting_value('userinfo');
2202
2203 // Define the elements
2204
2205 $areas = new backup_nested_element('areas');
2206
2207 $area = new backup_nested_element('area', array('id'), array(
2208 'areaname', 'activemethod'));
2209
2210 $definitions = new backup_nested_element('definitions');
2211
2212 $definition = new backup_nested_element('definition', array('id'), array(
2213 'method', 'name', 'description', 'descriptionformat', 'status',
2214 'timecreated', 'timemodified', 'options'));
2215
2216 $instances = new backup_nested_element('instances');
2217
2218 $instance = new backup_nested_element('instance', array('id'), array(
2219 'raterid', 'itemid', 'rawgrade', 'status', 'feedback',
2220 'feedbackformat', 'timemodified'));
2221
2222 // Build the tree including the method specific structures
2223 // (beware - the order of how gradingform plugins structures are attached is important)
2224 $areas->add_child($area);
a66e8bb3
SC
2225 // attach local plugin stucture to $area element, multiple allowed
2226 $this->add_plugin_structure('local', $area, true);
9b553d47
DM
2227 $area->add_child($definitions);
2228 $definitions->add_child($definition);
2229 $this->add_plugin_structure('gradingform', $definition, true);
a66e8bb3
SC
2230 // attach local plugin stucture to $definition element, multiple allowed
2231 $this->add_plugin_structure('local', $definition, true);
9b553d47
DM
2232 $definition->add_child($instances);
2233 $instances->add_child($instance);
2234 $this->add_plugin_structure('gradingform', $instance, false);
a66e8bb3
SC
2235 // attach local plugin stucture to $instance element, multiple allowed
2236 $this->add_plugin_structure('local', $instance, true);
9b553d47
DM
2237
2238 // Define data sources
2239
2240 $area->set_source_table('grading_areas', array('contextid' => backup::VAR_CONTEXTID,
2241 'component' => array('sqlparam' => 'mod_'.$this->get_task()->get_modulename())));
2242
2243 $definition->set_source_table('grading_definitions', array('areaid' => backup::VAR_PARENTID));
2244
2245 if ($userinfo) {
c309e4f0 2246 $instance->set_source_table('grading_instances', array('definitionid' => backup::VAR_PARENTID));
9b553d47
DM
2247 }
2248
2249 // Annotate references
2250 $definition->annotate_files('grading', 'description', 'id');
2251 $instance->annotate_ids('user', 'raterid');
2252
2253 // Return the root element
2254 return $areas;
2255 }
2256}
2257
2258
77547b46
EL
2259/**
2260 * structure step in charge of constructing the grades.xml file for all the grade items
2261 * and letters related to one activity
2262 */
2263class backup_activity_grades_structure_step extends backup_structure_step {
2264
03c39daa
DM
2265 /**
2266 * No grades on the front page.
2267 * @return bool
2268 */
2269 protected function execute_condition() {
2270 return ($this->get_courseid() != SITEID);
2271 }
2272
77547b46
EL
2273 protected function define_structure() {
2274
2275 // To know if we are including userinfo
2276 $userinfo = $this->get_setting_value('userinfo');
2277
2278 // Define each element separated
2279
2280 $book = new backup_nested_element('activity_gradebook');
2281
2282 $items = new backup_nested_element('grade_items');
2283
2284 $item = new backup_nested_element('grade_item', array('id'), array(
2285 'categoryid', 'itemname', 'itemtype', 'itemmodule',
2286 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
2287 'calculation', 'gradetype', 'grademax', 'grademin',
2288 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
b4087c31
FM
2289 'plusfactor', 'aggregationcoef', 'aggregationcoef2', 'weightoverride',
2290 'sortorder', 'display', 'decimals', 'hidden', 'locked', 'locktime',
77547b46
EL
2291 'needsupdate', 'timecreated', 'timemodified'));
2292
2293 $grades = new backup_nested_element('grade_grades');
2294
2295 $grade = new backup_nested_element('grade_grade', array('id'), array(
2296 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
2297 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
2298 'locked', 'locktime', 'exported', 'overridden',
2299 'excluded', 'feedback', 'feedbackformat', 'information',
8836ef8a
DW
2300 'informationformat', 'timecreated', 'timemodified',
2301 'aggregationstatus', 'aggregationweight'));
77547b46
EL
2302
2303 $letters = new backup_nested_element('grade_letters');
2304
2305 $letter = new backup_nested_element('grade_letter', 'id', array(
2306 'lowerboundary', 'letter'));
2307
2308 // Build the tree
2309
2310 $book->add_child($items);
2311 $items->add_child($item);
2312
2313 $item->add_child($grades);
2314 $grades->add_child($grade);
2315
2316 $book->add_child($letters);
2317 $letters->add_child($letter);
2318
2319 // Define sources
2320
315f6d8e
AD
2321 $item->set_source_sql("SELECT gi.*
2322 FROM {grade_items} gi
2323 JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
2324 WHERE bi.backupid = ?
2325 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
77547b46
EL
2326
2327 // This only happens if we are including user info
2328 if ($userinfo) {
2329 $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
2330 }
2331
2332 $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
2333
2334 // Annotations
2335
2336 $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
2337 $item->annotate_ids('outcome', 'outcomeid');
2338
2339 $grade->annotate_ids('user', 'userid');
2340 $grade->annotate_ids('user', 'usermodified');
2341
2342 // Return the root element (book)
2343
2344 return $book;
2345 }
2346}
bd39b6f2 2347
167eb033
FM
2348/**
2349 * Structure step in charge of constructing the grade history of an activity.
2350 *
2351 * This step is added to the task regardless of the setting 'grade_histories'.
2352 * The reason is to allow for a more flexible step in case the logic needs to be
2353 * split accross different settings to control the history of items and/or grades.
2354 */
2355class backup_activity_grade_history_structure_step extends backup_structure_step {
2356
03c39daa
DM
2357 /**
2358 * No grades on the front page.
2359 * @return bool
2360 */
2361 protected function execute_condition() {
2362 return ($this->get_courseid() != SITEID);
2363 }
2364
167eb033
FM
2365 protected function define_structure() {
2366
2367 // Settings to use.
2368 $userinfo = $this->get_setting_value('userinfo');
2369 $history = $this->get_setting_value('grade_histories');
2370
2371 // Create the nested elements.
2372 $bookhistory = new backup_nested_element('grade_history');
2373 $grades = new backup_nested_element('grade_grades');
2374 $grade = new backup_nested_element('grade_grade', array('id'), array(
2375 'action', 'oldid', 'source', 'loggeduser', 'itemid', 'userid',
2376 'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid',
2377 'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden',
2378 'excluded', 'feedback', 'feedbackformat', 'information',
2379 'informationformat', 'timemodified'));
2380
2381 // Build the tree.
2382 $bookhistory->add_child($grades);
2383 $grades->add_child($grade);
2384
2385 // This only happens if we are including user info and history.
2386 if ($userinfo && $history) {
2387 // Define sources. Only select the history related to existing activity items.
2388 $grade->set_source_sql("SELECT ggh.*
2389 FROM {grade_grades_history} ggh
2390 JOIN {backup_ids_temp} bi ON ggh.itemid = bi.itemid
2391 WHERE bi.backupid = ?
2392 AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
2393 }
2394
2395 // Annotations.
2396 $grade->annotate_ids('scalefinal', 'rawscaleid'); // Straight as scalefinal because it's > 0.
2397 $grade->annotate_ids('user', 'loggeduser');
2398 $grade->annotate_ids('user', 'userid');
2399 $grade->annotate_ids('user', 'usermodified');
2400
2401 // Return the root element.
2402 return $bookhistory;
2403 }
2404}
2405
bd39b6f2
SH
2406/**
2407 * Backups up the course completion information for the course.
2408 */
2409class backup_course_completion_structure_step extends backup_structure_step {
2410
2411 protected function execute_condition() {
03c39daa
DM
2412
2413 // No completion on front page.
2414 if ($this->get_courseid() == SITEID) {
2415 return false;
2416 }
2417
bd39b6f2
SH
2418 // Check that all activities have been included
2419 if ($this->task->is_excluding_activities()) {
2420 return false;
2421 }
2422 return true;
2423 }
2424
2425 /**
2426 * The structure of the course completion backup
2427 *
2428 * @return backup_nested_element
2429 */
2430 protected function define_structure() {
2431
2432 // To know if we are including user completion info
2433 $userinfo = $this->get_setting_value('userscompletion');
2434
2435 $cc = new backup_nested_element('course_completion');
2436
2437 $criteria = new backup_nested_element('course_completion_criteria', array('id'), array(
129e786c
AB
2438 'course', 'criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod',
2439 'timeend', 'gradepass', 'role', 'roleshortname'
bd39b6f2
SH
2440 ));
2441
2442 $criteriacompletions = new backup_nested_element('course_completion_crit_completions');
2443
2444 $criteriacomplete = new backup_nested_element('course_completion_crit_compl', array('id'), array(
46eca1f7 2445 'criteriaid', 'userid', 'gradefinal', 'unenrolled', 'timecompleted'
bd39b6f2
SH
2446 ));
2447
2448 $coursecompletions = new backup_nested_element('course_completions', array('id'), array(
46eca1f7 2449 'userid', 'course', 'timeenrolled', 'timestarted', 'timecompleted', 'reaggregate'
bd39b6f2
SH
2450 ));
2451
2452 $aggregatemethod = new backup_nested_element('course_completion_aggr_methd', array('id'), array(
2453 'course','criteriatype','method','value'
2454 ));
2455
2456 $cc->add_child($criteria);
2457 $criteria->add_child($criteriacompletions);
2458 $criteriacompletions->add_child($criteriacomplete);
2459 $cc->add_child($coursecompletions);
bd39b6f2
SH
2460 $cc->add_child($aggregatemethod);
2461
a43f43e2
AG
2462 // We need some extra data for the restore.
2463 // - courseinstances shortname rather than an ID.
2464 // - roleshortname in case restoring on a different site.
2465 $sourcesql = "SELECT ccc.*, c.shortname AS courseinstanceshortname, r.shortname AS roleshortname
2466 FROM {course_completion_criteria} ccc
2467 LEFT JOIN {course} c ON c.id = ccc.courseinstance
2468 LEFT JOIN {role} r ON r.id = ccc.role
2469 WHERE ccc.course = ?";
2470 $criteria->set_source_sql($sourcesql, array(backup::VAR_COURSEID));
bd39b6f2 2471
bd39b6f2
SH
2472 $aggregatemethod->set_source_table('course_completion_aggr_methd', array('course' => backup::VAR_COURSEID));
2473
2474 if ($userinfo) {
2475 $criteriacomplete->set_source_table('course_completion_crit_compl', array('criteriaid' => backup::VAR_PARENTID));
2476 $coursecompletions->set_source_table('course_completions', array('course' => backup::VAR_COURSEID));
2477 }
2478
2479 $criteria->annotate_ids('role', 'role');
2480 $criteriacomplete->annotate_ids('user', 'userid');
2481 $coursecompletions->annotate_ids('user', 'userid');
bd39b6f2
SH
2482
2483 return $cc;
2484
2485 }
767cb7f0 2486}