[ Index ]

PHP Cross Reference of moodle-2.8

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


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1