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  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This file contains the restore user interface class
  19   *
  20   * @package   core_backup
  21   * @copyright 2010 Sam Hemelryk
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * This is the restore user interface class
  27   *
  28   * The restore user interface class manages the user interface and restore for
  29   * Moodle.
  30   *
  31   * @package   core_backup
  32   * @copyright 2010 Sam Hemelryk
  33   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class restore_ui extends base_ui {
  36      /**
  37       * The stages of the restore user interface.
  38       * Confirm the backup you are going to restore.
  39       */
  40      const STAGE_CONFIRM = 1;
  41  
  42      /**
  43       * The stages of the restore user interface.
  44       * Select the destination for the restore.
  45       */
  46      const STAGE_DESTINATION = 2;
  47  
  48      /**
  49       * The stages of the restore user interface.
  50       * Alter the setting for the restore.
  51       */
  52      const STAGE_SETTINGS = 4;
  53  
  54      /**
  55       * The stages of the restore user interface.
  56       * Alter and review the schema that you are going to restore.
  57       */
  58      const STAGE_SCHEMA = 8;
  59  
  60      /**
  61       * The stages of the restore user interface.
  62       * The final review before the restore is run.
  63       */
  64      const STAGE_REVIEW = 16;
  65  
  66      /**
  67       * The stages of the restore user interface.
  68       * The restore is in process right now.
  69       */
  70      const STAGE_PROCESS = 32;
  71  
  72      /**
  73       * The stages of the restore user interface.
  74       * The process is complete.
  75       */
  76      const STAGE_COMPLETE = 64;
  77  
  78      /**
  79       * The current UI stage.
  80       * @var restore_ui_stage
  81       */
  82      protected $stage = null;
  83  
  84      /**
  85       * @var \core\progress\base Progress indicator (where there is no controller)
  86       */
  87      protected $progressreporter = null;
  88  
  89      /**
  90       * String mappings to the above stages
  91       * @var array
  92       */
  93      public static $stages = array(
  94          restore_ui::STAGE_CONFIRM       => 'confirm',
  95          restore_ui::STAGE_DESTINATION   => 'destination',
  96          restore_ui::STAGE_SETTINGS      => 'settings',
  97          restore_ui::STAGE_SCHEMA        => 'schema',
  98          restore_ui::STAGE_REVIEW        => 'review',
  99          restore_ui::STAGE_PROCESS       => 'process',
 100          restore_ui::STAGE_COMPLETE      => 'complete'
 101      );
 102  
 103      /**
 104       * Intialises what ever stage is requested. If none are requested we check
 105       * params for 'stage' and default to initial
 106       *
 107       * @throws restore_ui_exception for an invalid stage
 108       * @param int|null $stage The desired stage to intialise or null for the default
 109       * @param array $params
 110       * @return restore_ui_stage_initial|restore_ui_stage_schema|restore_ui_stage_confirmation|restore_ui_stage_final
 111       */
 112      protected function initialise_stage($stage = null, array $params = null) {
 113          if ($stage == null) {
 114              $stage = optional_param('stage', self::STAGE_CONFIRM, PARAM_INT);
 115          }
 116          $class = 'restore_ui_stage_'.self::$stages[$stage];
 117          if (!class_exists($class)) {
 118              throw new restore_ui_exception('unknownuistage');
 119          }
 120          $stage = new $class($this, $params);
 121          return $stage;
 122      }
 123  
 124      /**
 125       * This processes the current stage of the restore
 126       * @throws restore_ui_exception if the progress is wrong.
 127       * @return bool
 128       */
 129      public function process() {
 130          if ($this->progress >= self::PROGRESS_PROCESSED) {
 131              throw new restore_ui_exception('restoreuialreadyprocessed');
 132          }
 133          $this->progress = self::PROGRESS_PROCESSED;
 134  
 135          if (optional_param('previous', false, PARAM_BOOL) && $this->stage->get_stage() > self::STAGE_CONFIRM) {
 136              $this->stage = $this->initialise_stage($this->stage->get_prev_stage(), $this->stage->get_params());
 137              return false;
 138          }
 139  
 140          // Process the stage.
 141          $processoutcome = $this->stage->process();
 142          if ($processoutcome !== false && !($this->get_stage() == self::STAGE_PROCESS && optional_param('substage', false, PARAM_BOOL))) {
 143              $this->stage = $this->initialise_stage($this->stage->get_next_stage(), $this->stage->get_params());
 144          }
 145  
 146          // Process UI event after to check changes are valid.
 147          $this->controller->process_ui_event();
 148          return $processoutcome;
 149      }
 150  
 151      /**
 152       * Returns true if the stage is independent (not requiring a restore controller)
 153       * @return bool
 154       */
 155      public function is_independent() {
 156          return false;
 157      }
 158  
 159      /**
 160       * Gets the unique ID associated with this UI
 161       * @return string
 162       */
 163      public function get_uniqueid() {
 164          return $this->get_restoreid();
 165      }
 166  
 167      /**
 168       * Gets the restore id from the controller
 169       * @return string
 170       */
 171      public function get_restoreid() {
 172          return $this->controller->get_restoreid();
 173      }
 174  
 175      /**
 176       * Gets the progress reporter object in use for this restore UI.
 177       *
 178       * IMPORTANT: This progress reporter is used only for UI progress that is
 179       * outside the restore controller. The restore controller has its own
 180       * progress reporter which is used for progress during the main restore.
 181       * Use the restore controller's progress reporter to report progress during
 182       * a restore operation, not this one.
 183       *
 184       * This extra reporter is necessary because on some restore UI screens,
 185       * there are long-running tasks even though there is no restore controller
 186       * in use.
 187       *
 188       * @return \core\progress\none
 189       */
 190      public function get_progress_reporter() {
 191          if (!$this->progressreporter) {
 192              $this->progressreporter = new \core\progress\none();
 193          }
 194          return $this->progressreporter;
 195      }
 196  
 197      /**
 198       * Sets the progress reporter that will be returned by get_progress_reporter.
 199       *
 200       * @param \core\progress\base $progressreporter Progress reporter
 201       */
 202      public function set_progress_reporter(\core\progress\base $progressreporter) {
 203          $this->progressreporter = $progressreporter;
 204      }
 205  
 206      /**
 207       * Executes the restore plan
 208       * @throws restore_ui_exception if the progress or stage is wrong.
 209       * @return bool
 210       */
 211      public function execute() {
 212          if ($this->progress >= self::PROGRESS_EXECUTED) {
 213              throw new restore_ui_exception('restoreuialreadyexecuted');
 214          }
 215          if ($this->stage->get_stage() < self::STAGE_PROCESS) {
 216              throw new restore_ui_exception('restoreuifinalisedbeforeexecute');
 217          }
 218  
 219          $this->controller->execute_plan();
 220          $this->progress = self::PROGRESS_EXECUTED;
 221          $this->stage = new restore_ui_stage_complete($this, $this->stage->get_params(), $this->controller->get_results());
 222          return true;
 223      }
 224  
 225      /**
 226       * Delete course which is created by restore process
 227       */
 228      public function cleanup() {
 229          global $DB;
 230          $courseid = $this->controller->get_courseid();
 231          if ($this->is_temporary_course_created($courseid) && $course = $DB->get_record('course', array('id' => $courseid))) {
 232              $course->deletesource = 'restore';
 233              delete_course($course, false);
 234          }
 235      }
 236  
 237      /**
 238       * Checks if the course is not restored fully and current controller has created it.
 239       * @param int $courseid id of the course which needs to be checked
 240       * @return bool
 241       */
 242      protected function is_temporary_course_created($courseid) {
 243          global $DB;
 244          // Check if current controller instance has created new course.
 245          if ($this->controller->get_target() == backup::TARGET_NEW_COURSE) {
 246              $results = $DB->record_exists_sql("SELECT bc.itemid
 247                                                 FROM {backup_controllers} bc, {course} c
 248                                                 WHERE bc.operation = 'restore'
 249                                                   AND bc.type = 'course'
 250                                                   AND bc.itemid = c.id
 251                                                   AND bc.itemid = ?",
 252                                                 array($courseid)
 253                                               );
 254              return $results;
 255          }
 256          return false;
 257      }
 258  
 259      /**
 260       * Returns true if enforce_dependencies changed any settings
 261       * @return bool
 262       */
 263      public function enforce_changed_dependencies() {
 264          return ($this->dependencychanges > 0);
 265      }
 266  
 267      /**
 268       * Loads the restore controller if we are tracking one
 269       * @param string|bool $restoreid
 270       * @return string
 271       */
 272      final public static function load_controller($restoreid = false) {
 273          // Get the restore id optional param.
 274          if ($restoreid) {
 275              try {
 276                  // Try to load the controller with it.
 277                  // If it fails at this point it is likely because this is the first load.
 278                  $controller = restore_controller::load_controller($restoreid);
 279                  return $controller;
 280              } catch (Exception $e) {
 281                  return false;
 282              }
 283          }
 284          return $restoreid;
 285      }
 286  
 287      /**
 288       * Initialised the requested independent stage
 289       *
 290       * @throws restore_ui_exception
 291       * @param int $stage One of self::STAGE_*
 292       * @param int $contextid
 293       * @return restore_ui_stage_confirm|restore_ui_stage_destination
 294       */
 295      final public static function engage_independent_stage($stage, $contextid) {
 296          if (!($stage & self::STAGE_CONFIRM + self::STAGE_DESTINATION)) {
 297              throw new restore_ui_exception('dependentstagerequested');
 298          }
 299          $class = 'restore_ui_stage_'.self::$stages[$stage];
 300          if (!class_exists($class)) {
 301              throw new restore_ui_exception('unknownuistage');
 302          }
 303          return new $class($contextid);
 304      }
 305  
 306      /**
 307       * Cancels the current restore and redirects the user back to the relevant place
 308       */
 309      public function cancel_process() {
 310          // Delete temporary restore course if exists.
 311          if ($this->controller->get_target() == backup::TARGET_NEW_COURSE) {
 312              $this->cleanup();
 313          }
 314          parent::cancel_process();
 315      }
 316  
 317      /**
 318       * Gets an array of progress bar items that can be displayed through the restore renderer.
 319       * @return array Array of items for the progress bar
 320       */
 321      public function get_progress_bar() {
 322          global $PAGE;
 323  
 324          $stage = self::STAGE_COMPLETE;
 325          $currentstage = $this->stage->get_stage();
 326          $items = array();
 327          while ($stage > 0) {
 328              $classes = array('backup_stage');
 329              if (floor($stage / 2) == $currentstage) {
 330                  $classes[] = 'backup_stage_next';
 331              } else if ($stage == $currentstage) {
 332                  $classes[] = 'backup_stage_current';
 333              } else if ($stage < $currentstage) {
 334                  $classes[] = 'backup_stage_complete';
 335              }
 336              $item = array('text' => strlen(decbin($stage)).'. '.get_string('restorestage'.$stage, 'backup'), 'class' => join(' ', $classes));
 337              if ($stage < $currentstage && $currentstage < self::STAGE_COMPLETE && $stage > self::STAGE_DESTINATION) {
 338                  $item['link'] = new moodle_url($PAGE->url, array('restore' => $this->get_restoreid(), 'stage' => $stage));
 339              }
 340              array_unshift($items, $item);
 341              $stage = floor($stage / 2);
 342          }
 343          return $items;
 344      }
 345  
 346      /**
 347       * Gets the name of this UI
 348       * @return string
 349       */
 350      public function get_name() {
 351          return 'restore';
 352      }
 353  
 354      /**
 355       * Gets the first stage for this UI
 356       * @return int STAGE_CONFIRM
 357       */
 358      public function get_first_stage_id() {
 359          return self::STAGE_CONFIRM;
 360      }
 361  
 362      /**
 363       * Returns true if this stage has substages of which at least one needs to be displayed
 364       * @return bool
 365       */
 366      public function requires_substage() {
 367          return ($this->stage->has_sub_stages() && !$this->stage->process());
 368      }
 369  
 370      /**
 371       * Displays this stage
 372       *
 373       * @throws base_ui_exception if the progress is wrong.
 374       * @param core_backup_renderer $renderer
 375       * @return string HTML code to echo
 376       */
 377      public function display(core_backup_renderer $renderer) {
 378          if ($this->progress < self::PROGRESS_SAVED) {
 379              throw new base_ui_exception('backupsavebeforedisplay');
 380          }
 381          return $this->stage->display($renderer);
 382      }
 383  }
 384  
 385  /**
 386   * Restore user interface exception. Modelled off the restore_exception class
 387   *
 388   * @package   core_backup
 389   * @copyright 2010 Sam Hemelryk
 390   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 391   */
 392  class restore_ui_exception extends base_ui_exception {}