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