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