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.

Differences Between: [Versions 310 and 401]

   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   * Backup user interface stages
  19   *
  20   * This file contains the classes required to manage the stages that make up the
  21   * backup user interface.
  22   * These will be primarily operated a {@link backup_ui} instance.
  23   *
  24   * @package   core_backup
  25   * @copyright 2010 Sam Hemelryk
  26   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  
  29  /**
  30   * Abstract stage class
  31   *
  32   * This class should be extended by all backup stages (a requirement of many backup ui functions).
  33   * Each stage must then define two abstract methods
  34   *  - process : To process the stage
  35   *  - initialise_stage_form : To get a backup_moodleform instance for the stage
  36   *
  37   * @package   core_backup
  38   * @copyright 2010 Sam Hemelryk
  39   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  abstract class backup_ui_stage extends base_ui_stage {
  42  
  43      /**
  44       * Constructor.
  45       *
  46       * @param backup_ui $ui
  47       * @param array $params
  48       */
  49      public function __construct(backup_ui $ui, array $params = null) {
  50          parent::__construct($ui, $params);
  51      }
  52  
  53      /**
  54       * The backup id from the backup controller
  55       * @return string
  56       */
  57      final public function get_backupid() {
  58          return $this->get_uniqueid();
  59      }
  60  }
  61  
  62  /**
  63   * Class representing the initial stage of a backup.
  64   *
  65   * In this stage the user is required to set the root level settings.
  66   *
  67   * @package   core_backup
  68   * @copyright 2010 Sam Hemelryk
  69   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  70   */
  71  class backup_ui_stage_initial extends backup_ui_stage {
  72  
  73      /**
  74       * When set to true we skip all stages and jump to immediately processing the backup.
  75       * @var bool
  76       */
  77      protected $oneclickbackup = false;
  78  
  79      /**
  80       * Initial backup stage constructor
  81       * @param backup_ui $ui
  82       * @param array $params
  83       */
  84      public function __construct(backup_ui $ui, array $params = null) {
  85          $this->stage = backup_ui::STAGE_INITIAL;
  86          parent::__construct($ui, $params);
  87      }
  88  
  89      /**
  90       * Processes the initial backup stage
  91       * @param base_moodleform $m
  92       * @return int The number of changes
  93       */
  94      public function process(base_moodleform $m = null) {
  95  
  96          $form = $this->initialise_stage_form();
  97  
  98          if ($form->is_cancelled()) {
  99              $this->ui->cancel_process();
 100          }
 101  
 102          $data = $form->get_data();
 103          if ($data && confirm_sesskey()) {
 104              if (isset($data->oneclickbackup)) {
 105                  $this->oneclickbackup = true;
 106              }
 107              $tasks = $this->ui->get_tasks();
 108              $changes = 0;
 109              foreach ($tasks as &$task) {
 110                  // We are only interesting in the backup root task for this stage.
 111                  if ($task instanceof backup_root_task) {
 112                      // Get all settings into a var so we can iterate by reference.
 113                      $settings = $task->get_settings();
 114                      foreach ($settings as &$setting) {
 115                          $name = $setting->get_ui_name();
 116                          if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
 117                              $setting->set_value($data->$name);
 118                              $changes++;
 119                          } else if (!isset($data->$name) && $setting->get_value() &&
 120                                  $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX &&
 121                                  $setting->get_status() !== backup_setting::LOCKED_BY_HIERARCHY) {
 122                              $setting->set_value(0);
 123                              $changes++;
 124                          }
 125                      }
 126                  }
 127              }
 128              // Return the number of changes the user made.
 129              return $changes;
 130          } else {
 131              return false;
 132          }
 133      }
 134  
 135      /**
 136       * Gets the next stage for the backup.
 137       *
 138       * We override this function to implement the one click backup.
 139       * When the user performs a one click backup we jump straight to the final stage.
 140       *
 141       * @return int
 142       */
 143      public function get_next_stage() {
 144          if ($this->oneclickbackup) {
 145              // Its a one click backup.
 146              // The default filename is backup.mbz, this normally gets set to something useful in the confirmation stage.
 147              // because we skipped that stage we must manually set this to a useful value.
 148              $tasks = $this->ui->get_tasks();
 149              foreach ($tasks as $task) {
 150                  if ($task instanceof backup_root_task) {
 151                      // Find the filename setting.
 152                      $setting = $task->get_setting('filename');
 153                      if ($setting) {
 154                          // Use the helper objects to get a useful name.
 155                          $filename = backup_plan_dbops::get_default_backup_filename(
 156                              $this->ui->get_format(),
 157                              $this->ui->get_type(),
 158                              $this->ui->get_controller_id(),
 159                              $this->ui->get_setting_value('users'),
 160                              $this->ui->get_setting_value('anonymize'),
 161                              false,
 162                              (bool)$this->ui->get_setting_value('files')
 163                          );
 164                          $setting->set_value($filename);
 165                      }
 166                  }
 167              }
 168              return backup_ui::STAGE_FINAL;
 169          }
 170          return parent::get_next_stage();
 171      }
 172  
 173      /**
 174       * Initialises the backup_moodleform instance for this stage
 175       *
 176       * @return backup_initial_form
 177       */
 178      protected function initialise_stage_form() {
 179          global $PAGE;
 180          if ($this->stageform === null) {
 181              $form = new backup_initial_form($this, $PAGE->url);
 182              // Store as a variable so we can iterate by reference.
 183              $tasks = $this->ui->get_tasks();
 184              // Iterate all tasks by reference.
 185              $add_settings = array();
 186              $dependencies = array();
 187              foreach ($tasks as &$task) {
 188                  // For the initial stage we are only interested in the root settings.
 189                  if ($task instanceof backup_root_task) {
 190                      if ($this->ui instanceof import_ui) {
 191                          $form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
 192                      } else {
 193                          $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
 194                      }
 195                      $settings = $task->get_settings();
 196                      // First add all settings except the filename setting.
 197                      foreach ($settings as &$setting) {
 198                          if ($setting->get_name() == 'filename') {
 199                              continue;
 200                          }
 201                          $add_settings[] = array($setting, $task);
 202                      }
 203                      // Then add all dependencies.
 204                      foreach ($settings as &$setting) {
 205                          if ($setting->get_name() == 'filename') {
 206                              continue;
 207                          }
 208                          $dependencies[] = $setting;
 209                      }
 210                  }
 211              }
 212              // Add all settings at once.
 213              $form->add_settings($add_settings);
 214              // Add dependencies.
 215              foreach ($dependencies as $depsetting) {
 216                  $form->add_dependencies($depsetting);
 217              }
 218              $this->stageform = $form;
 219          }
 220          // Return the form.
 221          return $this->stageform;
 222      }
 223  }
 224  
 225  /**
 226   * Schema stage of backup process
 227   *
 228   * During the schema stage the user is required to set the settings that relate
 229   * to the area that they are backing up as well as its children.
 230   *
 231   * @package   core_backup
 232   * @copyright 2010 Sam Hemelryk
 233   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 234   */
 235  class backup_ui_stage_schema extends backup_ui_stage {
 236  
 237      /**
 238       * @var int Maximum number of settings to add to form at once
 239       */
 240      const MAX_SETTINGS_BATCH = 1000;
 241  
 242      /**
 243       * Schema stage constructor
 244       * @param backup_ui $ui
 245       * @param array $params
 246       */
 247      public function __construct(backup_ui $ui, array $params = null) {
 248          $this->stage = backup_ui::STAGE_SCHEMA;
 249          parent::__construct($ui, $params);
 250      }
 251  
 252      /**
 253       * Processes the schema stage
 254       *
 255       * @param base_moodleform $form
 256       * @return int The number of changes the user made
 257       */
 258      public function process(base_moodleform $form = null) {
 259          $form = $this->initialise_stage_form();
 260          // Check it wasn't cancelled.
 261          if ($form->is_cancelled()) {
 262              $this->ui->cancel_process();
 263          }
 264  
 265          // Check it has been submit.
 266          $data = $form->get_data();
 267          if ($data && confirm_sesskey()) {
 268              // Get the tasks into a var so we can iterate by reference.
 269              $tasks = $this->ui->get_tasks();
 270              $changes = 0;
 271              // Iterate all tasks by reference.
 272              foreach ($tasks as &$task) {
 273                  // We are only interested in schema settings.
 274                  if (!($task instanceof backup_root_task)) {
 275                      // Store as a variable so we can iterate by reference.
 276                      $settings = $task->get_settings();
 277                      // Iterate by reference.
 278                      foreach ($settings as &$setting) {
 279                          $name = $setting->get_ui_name();
 280                          if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
 281                              $setting->set_value($data->$name);
 282                              $changes++;
 283                          } else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
 284                              $setting->set_value(0);
 285                              $changes++;
 286                          }
 287                      }
 288                  }
 289              }
 290              // Return the number of changes the user made.
 291              return $changes;
 292          } else {
 293              return false;
 294          }
 295      }
 296  
 297      /**
 298       * Creates the backup_schema_form instance for this stage
 299       *
 300       * @return backup_schema_form
 301       */
 302      protected function initialise_stage_form() {
 303          global $PAGE;
 304          if ($this->stageform === null) {
 305              $form = new backup_schema_form($this, $PAGE->url);
 306              $tasks = $this->ui->get_tasks();
 307              $content = '';
 308              $courseheading = false;
 309              $add_settings = array();
 310              $dependencies = array();
 311  
 312              // Track progress through each stage.
 313              $progress = $this->ui->get_controller()->get_progress();
 314              $progress->start_progress('Initialise stage form', 3);
 315  
 316              // Get settings for all tasks.
 317              $progress->start_progress('', count($tasks));
 318              $done = 1;
 319              foreach ($tasks as $task) {
 320                  if (!($task instanceof backup_root_task)) {
 321                      if (!$courseheading) {
 322                          // If we haven't already display a course heading to group nicely.
 323                          $form->add_heading('coursesettings', get_string('includeactivities', 'backup'));
 324                          $courseheading = true;
 325                      }
 326                      // First add each setting.
 327                      foreach ($task->get_settings() as $setting) {
 328                          $add_settings[] = array($setting, $task);
 329                      }
 330                      // The add all the dependencies.
 331                      foreach ($task->get_settings() as $setting) {
 332                          $dependencies[] = $setting;
 333                      }
 334                  } else if ($this->ui->enforce_changed_dependencies()) {
 335                      // Only show these settings if dependencies changed them.
 336                      // Add a root settings heading to group nicely.
 337                      $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
 338                      // Iterate all settings and add them to the form as a fixed
 339                      // setting. We only want schema settings to be editable.
 340                      foreach ($task->get_settings() as $setting) {
 341                          if ($setting->get_name() != 'filename') {
 342                              $form->add_fixed_setting($setting, $task);
 343                          }
 344                      }
 345                  }
 346                  // Update progress.
 347                  $progress->progress($done++);
 348              }
 349              $progress->end_progress();
 350  
 351              // Add settings for tasks in batches of up to 1000. Adding settings
 352              // in larger batches improves performance, but if it takes too long,
 353              // we won't be able to update the progress bar so the backup might.
 354              // time out. 1000 is chosen to balance this.
 355              $numsettings = count($add_settings);
 356              $progress->start_progress('', ceil($numsettings / self::MAX_SETTINGS_BATCH));
 357              $start = 0;
 358              $done = 1;
 359              while ($start < $numsettings) {
 360                  $length = min(self::MAX_SETTINGS_BATCH, $numsettings - $start);
 361                  $form->add_settings(array_slice($add_settings, $start, $length));
 362                  $start += $length;
 363                  $progress->progress($done++);
 364              }
 365              $progress->end_progress();
 366  
 367              $progress->start_progress('', count($dependencies));
 368              $done = 1;
 369              foreach ($dependencies as $depsetting) {
 370                  $form->add_dependencies($depsetting);
 371                  $progress->progress($done++);
 372              }
 373              $progress->end_progress();
 374  
 375              // End overall progress through creating form.
 376              $progress->end_progress();
 377              $this->stageform = $form;
 378          }
 379          return $this->stageform;
 380      }
 381  }
 382  
 383  /**
 384   * Confirmation stage
 385   *
 386   * On this stage the user reviews the setting for the backup and can change the filename
 387   * of the file that will be generated.
 388   *
 389   * @package   core_backup
 390   * @copyright 2010 Sam Hemelryk
 391   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 392   */
 393  class backup_ui_stage_confirmation extends backup_ui_stage {
 394  
 395      /**
 396       * Constructs the stage
 397       * @param backup_ui $ui
 398       * @param array $params
 399       */
 400      public function __construct($ui, array $params = null) {
 401          $this->stage = backup_ui::STAGE_CONFIRMATION;
 402          parent::__construct($ui, $params);
 403      }
 404  
 405      /**
 406       * Processes the confirmation stage
 407       *
 408       * @param base_moodleform $form
 409       * @return int The number of changes the user made
 410       */
 411      public function process(base_moodleform $form = null) {
 412          $form = $this->initialise_stage_form();
 413          // Check it hasn't been cancelled.
 414          if ($form->is_cancelled()) {
 415              $this->ui->cancel_process();
 416          }
 417  
 418          $data = $form->get_data();
 419          if ($data && confirm_sesskey()) {
 420              // Collect into a variable so we can iterate by reference.
 421              $tasks = $this->ui->get_tasks();
 422              $changes = 0;
 423              // Iterate each task by reference.
 424              foreach ($tasks as &$task) {
 425                  if ($task instanceof backup_root_task) {
 426                      // At this stage all we are interested in is the filename setting.
 427                      $setting = $task->get_setting('filename');
 428                      $name = $setting->get_ui_name();
 429                      if (isset($data->$name) &&  $data->$name != $setting->get_value()) {
 430                          $setting->set_value($data->$name);
 431                          $changes++;
 432                      }
 433                  }
 434              }
 435              // Return the number of changes the user made.
 436              return $changes;
 437          } else {
 438              return false;
 439          }
 440      }
 441  
 442      /**
 443       * Creates the backup_confirmation_form instance this stage requires
 444       *
 445       * @return backup_confirmation_form
 446       */
 447      protected function initialise_stage_form() {
 448          global $PAGE;
 449          if ($this->stageform === null) {
 450              // Get the form.
 451              $form = new backup_confirmation_form($this, $PAGE->url);
 452              $content = '';
 453              $courseheading = false;
 454  
 455              foreach ($this->ui->get_tasks() as $task) {
 456                  if ($setting = $task->get_setting('filename')) {
 457                      $form->add_heading('filenamesetting', get_string('filename', 'backup'));
 458                      if ($setting->get_value() == 'backup.mbz') {
 459                          $format = $this->ui->get_format();
 460                          $type = $this->ui->get_type();
 461                          $id = $this->ui->get_controller_id();
 462                          $users = $this->ui->get_setting_value('users');
 463                          $anonymised = $this->ui->get_setting_value('anonymize');
 464                          $files = (bool)$this->ui->get_setting_value('files');
 465                          $filename = backup_plan_dbops::get_default_backup_filename(
 466                                  $format,
 467                                  $type,
 468                                  $id,
 469                                  $users,
 470                                  $anonymised,
 471                                  false,
 472                                  $files);
 473                          $setting->set_value($filename);
 474                      }
 475                      $form->add_setting($setting, $task);
 476                      break;
 477                  }
 478              }
 479  
 480              // Track progress through tasks.
 481              $progress = $this->ui->get_controller()->get_progress();
 482              $tasks = $this->ui->get_tasks();
 483              $progress->start_progress('initialise_stage_form', count($tasks));
 484              $done = 1;
 485  
 486              foreach ($tasks as $task) {
 487                  if ($task instanceof backup_root_task) {
 488                      // If its a backup root add a root settings heading to group nicely.
 489                      if ($this->ui instanceof import_ui) {
 490                          $form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
 491                      } else {
 492                          $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
 493                      }
 494                  } else if (!$courseheading) {
 495                      // We haven't already add a course heading.
 496                      $form->add_heading('coursesettings', get_string('includeditems', 'backup'));
 497                      $courseheading = true;
 498                  }
 499                  // Iterate all settings, doesnt need to happen by reference.
 500                  foreach ($task->get_settings() as $setting) {
 501                      // For this stage only the filename setting should be editable.
 502                      if ($setting->get_name() != 'filename') {
 503                          $form->add_fixed_setting($setting, $task);
 504                      }
 505                  }
 506                  // Update progress.
 507                  $progress->progress($done++);
 508              }
 509              $progress->end_progress();
 510              $this->stageform = $form;
 511          }
 512          return $this->stageform;
 513      }
 514  }
 515  
 516  /**
 517   * Final stage of backup
 518   *
 519   * This stage is special in that it is does not make use of a form. The reason for
 520   * this is the order of procession of backup at this stage.
 521   * The processesion is:
 522   * 1. The final stage will be intialise.
 523   * 2. The confirmation stage will be processed.
 524   * 3. The backup will be executed
 525   * 4. The complete stage will be loaded by execution
 526   * 5. The complete stage will be displayed
 527   *
 528   * This highlights that we neither need a form nor a display method for this stage
 529   * we simply need to process.
 530   *
 531   * @package   core_backup
 532   * @copyright 2010 Sam Hemelryk
 533   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 534   */
 535  class backup_ui_stage_final extends backup_ui_stage {
 536  
 537      /**
 538       * Constructs the final stage
 539       * @param backup_ui $ui
 540       * @param array $params
 541       */
 542      public function __construct(backup_ui $ui, array $params = null) {
 543          $this->stage = backup_ui::STAGE_FINAL;
 544          parent::__construct($ui, $params);
 545      }
 546  
 547      /**
 548       * Processes the final stage.
 549       *
 550       * In this case it ALWAYS passes processing to the previous stage (confirmation)
 551       *
 552       * @param base_moodleform $form
 553       * @return bool
 554       */
 555      public function process(base_moodleform $form = null) {
 556          return true;
 557      }
 558  
 559      /**
 560       * should NEVER be called... throws an exception
 561       */
 562      protected function initialise_stage_form() {
 563          throw new backup_ui_exception('backup_ui_must_execute_first');
 564      }
 565  
 566      /**
 567       * should NEVER be called... throws an exception
 568       *
 569       * @throws backup_ui_exception always
 570       * @param core_backup_renderer $renderer
 571       * @return void
 572       */
 573      public function display(core_backup_renderer $renderer) {
 574          throw new backup_ui_exception('backup_ui_must_execute_first');
 575      }
 576  }
 577  
 578  /**
 579   * The completed backup stage
 580   *
 581   * At this stage everything is done and the user will be redirected to view the
 582   * backup file in the file browser.
 583   *
 584   * @package   core_backup
 585   * @copyright 2010 Sam Hemelryk
 586   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 587   */
 588  class backup_ui_stage_complete extends backup_ui_stage_final {
 589  
 590      /**
 591       * The results of the backup execution
 592       * @var array
 593       */
 594      protected $results;
 595  
 596      /**
 597       * Constructs the complete backup stage
 598       *
 599       * @param backup_ui $ui
 600       * @param array $params
 601       * @param array $results
 602       */
 603      public function __construct(backup_ui $ui, array $params = null, array $results = null) {
 604          $this->results = $results;
 605          parent::__construct($ui, $params);
 606          $this->stage = backup_ui::STAGE_COMPLETE;
 607      }
 608  
 609      /**
 610       * Displays the completed backup stage.
 611       *
 612       * Currently this just involves redirecting to the file browser with an
 613       * appropriate message.
 614       *
 615       * @param core_backup_renderer $renderer
 616       * @return string HTML code to echo
 617       */
 618      public function display(core_backup_renderer $renderer) {
 619  
 620          // Get the resulting stored_file record.
 621          $type = $this->get_ui()->get_controller()->get_type();
 622          $courseid = $this->get_ui()->get_controller()->get_courseid();
 623          switch ($type) {
 624              case 'activity':
 625                  $cmid = $this->get_ui()->get_controller()->get_id();
 626                  $cm = get_coursemodule_from_id(null, $cmid, $courseid);
 627                  $modcontext = context_module::instance($cm->id);
 628                  $restorerul = new moodle_url('/backup/restorefile.php', array('contextid' => $modcontext->id));
 629                  break;
 630              case 'course':
 631              default:
 632                  $coursecontext = context_course::instance($courseid);
 633                  $restorerul = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
 634          }
 635  
 636          $output = '';
 637          $output .= $renderer->box_start();
 638          if (!empty($this->results['include_file_references_to_external_content'])) {
 639              $output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
 640          }
 641          if (!empty($this->results['missing_files_in_pool'])) {
 642              $output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
 643          }
 644          $output .= $renderer->get_samesite_notification();
 645          $output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
 646          $output .= $renderer->continue_button($restorerul, 'get');
 647          $output .= $renderer->box_end();
 648  
 649          return $output;
 650      }
 651  }