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