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