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