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