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.
/backup/ -> backup.php (source)

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   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   * This script is used to configure and execute the backup proccess.
  20   *
  21   * @package    core
  22   * @subpackage backup
  23   * @copyright  Moodle
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  define('NO_OUTPUT_BUFFERING', true);
  28  
  29  require_once('../config.php');
  30  require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
  31  require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
  32  
  33  // Backup of large courses requires extra memory. Use the amount configured
  34  // in admin settings.
  35  raise_memory_limit(MEMORY_EXTRA);
  36  
  37  $courseid = required_param('id', PARAM_INT);
  38  $sectionid = optional_param('section', null, PARAM_INT);
  39  $cmid = optional_param('cm', null, PARAM_INT);
  40  $cancel      = optional_param('cancel', '', PARAM_ALPHA);
  41  $previous = optional_param('previous', false, PARAM_BOOL);
  42  /**
  43   * Part of the forms in stages after initial, is POST never GET
  44   */
  45  $backupid = optional_param('backup', false, PARAM_ALPHANUM);
  46  
  47  // Determine if we are performing realtime for asynchronous backups.
  48  $backupmode = backup::MODE_GENERAL;
  49  if (async_helper::is_async_enabled()) {
  50      $backupmode = backup::MODE_ASYNC;
  51  }
  52  
  53  $courseurl = new moodle_url('/course/view.php', array('id' => $courseid));
  54  $url = new moodle_url('/backup/backup.php', array('id'=>$courseid));
  55  if ($sectionid !== null) {
  56      $url->param('section', $sectionid);
  57  }
  58  if ($cmid !== null) {
  59      $url->param('cm', $cmid);
  60  }
  61  $PAGE->set_url($url);
  62  $PAGE->set_pagelayout('admin');
  63  $id = $courseid;
  64  $cm = null;
  65  $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
  66  $coursecontext = context_course::instance($course->id);
  67  $contextid = $coursecontext->id;
  68  $type = backup::TYPE_1COURSE;
  69  if (!is_null($sectionid)) {
  70      $section = $DB->get_record('course_sections', array('course'=>$course->id, 'id'=>$sectionid), '*', MUST_EXIST);
  71      $type = backup::TYPE_1SECTION;
  72      $id = $sectionid;
  73  }
  74  if (!is_null($cmid)) {
  75      $cm = get_coursemodule_from_id(null, $cmid, $course->id, false, MUST_EXIST);
  76      $type = backup::TYPE_1ACTIVITY;
  77      $id = $cmid;
  78  }
  79  require_login($course, false, $cm);
  80  
  81  switch ($type) {
  82      case backup::TYPE_1COURSE :
  83          require_capability('moodle/backup:backupcourse', $coursecontext);
  84          $heading = get_string('backupcourse', 'backup', $course->shortname);
  85          $PAGE->set_secondary_active_tab('coursereuse');
  86          break;
  87      case backup::TYPE_1SECTION :
  88          require_capability('moodle/backup:backupsection', $coursecontext);
  89          if ((string)$section->name !== '') {
  90              $sectionname = format_string($section->name, true, array('context' => $coursecontext));
  91              $heading = get_string('backupsection', 'backup', $sectionname);
  92              $PAGE->navbar->add($sectionname);
  93          } else {
  94              $heading = get_string('backupsection', 'backup', $section->section);
  95              $PAGE->navbar->add(get_string('section').' '.$section->section);
  96          }
  97          break;
  98      case backup::TYPE_1ACTIVITY :
  99          $activitycontext = context_module::instance($cm->id);
 100          require_capability('moodle/backup:backupactivity', $activitycontext);
 101          $contextid = $activitycontext->id;
 102          $heading = get_string('backupactivity', 'backup', $cm->name);
 103          break;
 104      default :
 105          throw new \moodle_exception('unknownbackuptype');
 106  }
 107  
 108  $PAGE->set_title($heading);
 109  $PAGE->set_heading($heading);
 110  $PAGE->activityheader->disable();
 111  
 112  if (empty($cancel)) {
 113      // Do not print the header if user cancelled the process, as we are going to redirect the user.
 114      echo $OUTPUT->header();
 115  }
 116  
 117  // Only let user perform a backup if we aren't in async mode, or if we are
 118  // and there are no pending backups for this item for this user.
 119  if (!async_helper::is_async_pending($id, 'course', 'backup')) {
 120  
 121      // The mix of business logic and display elements below makes me sad.
 122      // This needs to refactored into the renderer and seperated out.
 123  
 124      if (!($bc = backup_ui::load_controller($backupid))) {
 125          $bc = new backup_controller($type, $id, backup::FORMAT_MOODLE,
 126                  backup::INTERACTIVE_YES, $backupmode, $USER->id, backup::RELEASESESSION_YES);
 127          // The backup id did not relate to a valid controller so we made a new controller.
 128          // Now we need to reset the backup id to match the new controller.
 129          $backupid = $bc->get_backupid();
 130      }
 131  
 132      // Prepare a progress bar which can display optionally during long-running
 133      // operations while setting up the UI.
 134      $slowprogress = new \core\progress\display_if_slow(get_string('preparingui', 'backup'));
 135      $renderer = $PAGE->get_renderer('core', 'backup');
 136      $backup = new backup_ui($bc);
 137  
 138      if ($backup->get_stage() == backup_ui::STAGE_SCHEMA && !$previous) {
 139          // After schema stage, we are probably going to get to the confirmation stage,
 140          // The confirmation stage has 2 sets of progress, so this is needed to prevent
 141          // it showing 2 progress bars.
 142          $twobars = true;
 143          $slowprogress->start_progress('', 2);
 144      } else {
 145          $twobars = false;
 146      }
 147      $backup->get_controller()->set_progress($slowprogress);
 148      $backup->process();
 149  
 150      if ($backup->enforce_changed_dependencies()) {
 151          debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
 152      }
 153  
 154      $loghtml = '';
 155      if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
 156  
 157          // Before we perform the backup check settings to see if user
 158          // or setting defaults are set to exclude files from the backup.
 159          if ($backup->get_setting_value('files') == 0) {
 160              $renderer->set_samesite_notification();
 161          }
 162  
 163          if ($backupmode != backup::MODE_ASYNC) {
 164              // Synchronous backup handling.
 165  
 166              // Display an extra backup step bar so that we can show the 'processing' step first.
 167              echo html_writer::start_div('', array('id' => 'executionprogress'));
 168              echo $renderer->progress_bar($backup->get_progress_bar());
 169              $backup->get_controller()->set_progress(new \core\progress\display());
 170  
 171              // Prepare logger and add to end of chain.
 172              $logger = new core_backup_html_logger($CFG->debugdeveloper ? backup::LOG_DEBUG : backup::LOG_INFO);
 173              $backup->get_controller()->add_logger($logger);
 174  
 175              // Carry out actual backup.
 176              $backup->execute();
 177  
 178              // Backup controller gets saved/loaded so the logger object changes and we
 179              // have to retrieve it.
 180              $logger = $backup->get_controller()->get_logger();
 181              while (!is_a($logger, 'core_backup_html_logger')) {
 182                  $logger = $logger->get_next();
 183              }
 184  
 185              // Get HTML from logger.
 186              if ($CFG->debugdisplay) {
 187                  $loghtml = $logger->get_html();
 188              }
 189  
 190              // Hide the progress display and first backup step bar (the 'finished' step will show next).
 191              echo html_writer::end_div();
 192              echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
 193  
 194          } else {
 195              // Async backup handling.
 196              $backup->get_controller()->finish_ui();
 197  
 198              echo html_writer::start_div('', array('id' => 'executionprogress'));
 199              echo $renderer->progress_bar($backup->get_progress_bar());
 200              echo html_writer::end_div();
 201  
 202              // Create adhoc task for backup.
 203              $asynctask = new \core\task\asynchronous_backup_task();
 204              $asynctask->set_blocking(false);
 205              $asynctask->set_custom_data(array('backupid' => $backupid));
 206              $asynctask->set_userid($USER->id);
 207              \core\task\manager::queue_adhoc_task($asynctask);
 208  
 209              // Add ajax progress bar and initiate ajax via a template.
 210              $restoreurl = new moodle_url('/backup/restorefile.php', array('contextid' => $contextid));
 211              $progresssetup = array(
 212                      'backupid' => $backupid,
 213                      'contextid' => $contextid,
 214                      'courseurl' => $courseurl->out(),
 215                      'restoreurl' => $restoreurl->out(),
 216                      'headingident' => 'backup'
 217              );
 218  
 219              echo $renderer->render_from_template('core/async_backup_status', $progresssetup);
 220          }
 221  
 222      } else {
 223          $backup->save_controller();
 224      }
 225  
 226      if ($backup->get_stage() != backup_ui::STAGE_FINAL) {
 227  
 228          // Displaying UI can require progress reporting, so do it here before outputting
 229          // the backup stage bar (as part of the existing progress bar, if required).
 230          $ui = $backup->display($renderer);
 231          if ($twobars) {
 232              $slowprogress->end_progress();
 233          }
 234  
 235          echo $renderer->progress_bar($backup->get_progress_bar());
 236          echo $ui;
 237  
 238          // Display log data if there was any.
 239          if ($loghtml != '' && $backupmode != backup::MODE_ASYNC) {
 240              echo $renderer->log_display($loghtml);
 241          }
 242      }
 243  
 244      $backup->destroy();
 245      unset($backup);
 246  
 247  } else { // User has a pending async operation.
 248      echo $OUTPUT->notification(get_string('pendingasyncerror', 'backup'), 'error');
 249      echo $OUTPUT->container(get_string('pendingasyncdetail', 'backup'));
 250      echo $OUTPUT->continue_button($courseurl);
 251  }
 252  
 253  echo $OUTPUT->footer();