[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/backup/moodle2/ -> backup_stepslib.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1