Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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   * @package    moodlecore
  20   * @subpackage backup-helper
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * Non instantiable helper class providing support for restore prechecks
  27   *
  28   * This class contains various prechecks to be performed before executing
  29   * the restore plan. Its entry point is execute_prechecks() that will
  30   * call various stuff. At the end, it will return one array(), if empty
  31   * all the prechecks have passed ok. If not empty, you'll find 1/2 elements
  32   * in the array, warnings and errors, each one containing one description
  33   * of the problem. Warnings aren't stoppers so the restore execution can
  34   * continue after displaying them. In the other side, if errors are returned
  35   * then restore execution cannot continue
  36   *
  37   * TODO: Finish phpdocs
  38   */
  39  abstract class restore_prechecks_helper {
  40  
  41      /**
  42       * Entry point for all the prechecks to be performed before restore
  43       *
  44       * Returns empty array or warnings/errors array
  45       */
  46      public static function execute_prechecks(restore_controller $controller, $droptemptablesafter = false) {
  47          global $CFG;
  48  
  49          $errors = array();
  50          $warnings = array();
  51  
  52          // Some handy vars to be used along the prechecks
  53          $samesite = $controller->is_samesite();
  54          $restoreusers = $controller->get_plan()->get_setting('users')->get_value();
  55          $hasmnetusers = (int)$controller->get_info()->mnet_remoteusers;
  56          $restoreid = $controller->get_restoreid();
  57          $courseid = $controller->get_courseid();
  58          $userid = $controller->get_userid();
  59          $rolemappings = $controller->get_info()->role_mappings;
  60          $progress = $controller->get_progress();
  61  
  62          // Start tracking progress. There are currently 8 major steps, corresponding
  63          // to $majorstep++ lines in this code; we keep track of the total so as to
  64          // verify that it's still correct. If you add a major step, you need to change
  65          // the total here.
  66          $majorstep = 1;
  67          $majorsteps = 8;
  68          $progress->start_progress('Carrying out pre-restore checks', $majorsteps);
  69  
  70          // Load all the included tasks to look for inforef.xml files
  71          $inforeffiles = array();
  72          $tasks = restore_dbops::get_included_tasks($restoreid);
  73          $progress->start_progress('Listing inforef files', count($tasks));
  74          $minorstep = 1;
  75          foreach ($tasks as $task) {
  76              // Add the inforef.xml file if exists
  77              $inforefpath = $task->get_taskbasepath() . '/inforef.xml';
  78              if (file_exists($inforefpath)) {
  79                  $inforeffiles[] = $inforefpath;
  80              }
  81              $progress->progress($minorstep++);
  82          }
  83          $progress->end_progress();
  84          $progress->progress($majorstep++);
  85  
  86          // Create temp tables
  87          restore_controller_dbops::create_restore_temp_tables($controller->get_restoreid());
  88  
  89          // Check we are restoring one backup >= $min20version (very first ok ever)
  90          $min20version = 2010072300;
  91          if ($controller->get_info()->backup_version < $min20version) {
  92              $message = new stdclass();
  93              $message->backup = $controller->get_info()->backup_version;
  94              $message->min    = $min20version;
  95              $errors[] = get_string('errorminbackup20version', 'backup', $message);
  96          }
  97  
  98          // Compare Moodle's versions
  99          if ($CFG->version < $controller->get_info()->moodle_version) {
 100              $message = new stdclass();
 101              $message->serverversion = $CFG->version;
 102              $message->serverrelease = $CFG->release;
 103              $message->backupversion = $controller->get_info()->moodle_version;
 104              $message->backuprelease = $controller->get_info()->moodle_release;
 105              $warnings[] = get_string('noticenewerbackup','',$message);
 106          }
 107  
 108          // The original_course_format var was introduced in Moodle 2.9.
 109          $originalcourseformat = null;
 110          if (!empty($controller->get_info()->original_course_format)) {
 111              $originalcourseformat = $controller->get_info()->original_course_format;
 112          }
 113  
 114          // We can't restore other course's backups on the front page.
 115          if ($controller->get_courseid() == SITEID &&
 116                  $originalcourseformat != 'site' &&
 117                  $controller->get_type() == backup::TYPE_1COURSE) {
 118              $errors[] = get_string('errorrestorefrontpagebackup', 'backup');
 119          }
 120  
 121          // We can't restore front pages over other courses.
 122          if ($controller->get_courseid() != SITEID &&
 123                  $originalcourseformat == 'site' &&
 124                  $controller->get_type() == backup::TYPE_1COURSE) {
 125              $errors[] = get_string('errorrestorefrontpagebackup', 'backup');
 126          }
 127  
 128          // If restoring to different site and restoring users and backup has mnet users warn/error
 129          if (!$samesite && $restoreusers && $hasmnetusers) {
 130              // User is admin (can create users at sysctx), warn
 131              if (has_capability('moodle/user:create', context_system::instance(), $controller->get_userid())) {
 132                  $warnings[] = get_string('mnetrestore_extusers_admin', 'admin');
 133              // User not admin
 134              } else {
 135                  $errors[] = get_string('mnetrestore_extusers_noadmin', 'admin');
 136              }
 137          }
 138  
 139          // Load all the inforef files, we are going to need them
 140          $progress->start_progress('Loading temporary IDs', count($inforeffiles));
 141          $minorstep = 1;
 142          foreach ($inforeffiles as $inforeffile) {
 143              // Load each inforef file to temp_ids.
 144              restore_dbops::load_inforef_to_tempids($restoreid, $inforeffile, $progress);
 145              $progress->progress($minorstep++);
 146          }
 147          $progress->end_progress();
 148          $progress->progress($majorstep++);
 149  
 150          // If restoring users, check we are able to create all them
 151          if ($restoreusers) {
 152              $file = $controller->get_plan()->get_basepath() . '/users.xml';
 153               // Load needed users to temp_ids.
 154              restore_dbops::load_users_to_tempids($restoreid, $file, $progress);
 155              $progress->progress($majorstep++);
 156              if ($problems = restore_dbops::precheck_included_users($restoreid, $courseid, $userid, $samesite, $progress)) {
 157                  $errors = array_merge($errors, $problems);
 158              }
 159          } else {
 160              // To ensure consistent number of steps in progress tracking,
 161              // mark progress even though we didn't do anything.
 162              $progress->progress($majorstep++);
 163          }
 164          $progress->progress($majorstep++);
 165  
 166          // Note: restore won't create roles at all. Only mapping/skip!
 167          $file = $controller->get_plan()->get_basepath() . '/roles.xml';
 168          restore_dbops::load_roles_to_tempids($restoreid, $file); // Load needed roles to temp_ids
 169          if ($problems = restore_dbops::precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings)) {
 170              $errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
 171              $warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
 172          }
 173          $progress->progress($majorstep++);
 174  
 175          // Check we are able to restore and the categories and questions
 176          $file = $controller->get_plan()->get_basepath() . '/questions.xml';
 177          restore_dbops::load_categories_and_questions_to_tempids($restoreid, $file);
 178          if ($problems = restore_dbops::precheck_categories_and_questions($restoreid, $courseid, $userid, $samesite)) {
 179              $errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
 180              $warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
 181          }
 182          $progress->progress($majorstep++);
 183  
 184          // Prepare results.
 185          $results = array();
 186          if (!empty($errors)) {
 187              $results['errors'] = $errors;
 188          }
 189          if (!empty($warnings)) {
 190              $results['warnings'] = $warnings;
 191          }
 192          // Warnings/errors detected or want to do so explicitly, drop temp tables
 193          if (!empty($results) || $droptemptablesafter) {
 194              restore_controller_dbops::drop_restore_temp_tables($controller->get_restoreid());
 195          }
 196  
 197          // Finish progress and check we got the initial number of steps right.
 198          $progress->progress($majorstep++);
 199          if ($majorstep != $majorsteps) {
 200              throw new coding_exception('Progress step count wrong: ' . $majorstep);
 201          }
 202          $progress->end_progress();
 203  
 204          return $results;
 205      }
 206  }
 207  
 208  /*
 209   * Exception class used by all the @restore_prechecks_helper stuff
 210   */
 211  class restore_prechecks_helper_exception extends backup_exception {
 212  
 213      public function __construct($errorcode, $a=NULL, $debuginfo=null) {
 214          parent::__construct($errorcode, $a, $debuginfo);
 215      }
 216  }