Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
/backup/ -> backup.php (source)

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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  
  64  $id = $courseid;
  65  $cm = null;
  66  $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
  67  $coursecontext = context_course::instance($course->id);
  68  $contextid = $coursecontext->id;
  69  $type = backup::TYPE_1COURSE;
  70  if (!is_null($sectionid)) {
  71      $section = $DB->get_record('course_sections', array('course'=>$course->id, 'id'=>$sectionid), '*', MUST_EXIST);
  72      $type = backup::TYPE_1SECTION;
  73      $id = $sectionid;
  74  }
  75  if (!is_null($cmid)) {
  76      $cm = get_coursemodule_from_id(null, $cmid, $course->id, false, MUST_EXIST);
  77      $type = backup::TYPE_1ACTIVITY;
  78      $id = $cmid;
  79  }
  80  require_login($course, false, $cm);
  81  
  82  switch ($type) {
  83      case backup::TYPE_1COURSE :
  84          require_capability('moodle/backup:backupcourse', $coursecontext);
  85          $heading = get_string('backupcourse', 'backup', $course->shortname);
  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          print_error('unknownbackuptype');
 106  }
 107  
 108  $PAGE->set_title($heading);
 109  $PAGE->set_heading($heading);
 110  
 111  if (empty($cancel)) {
 112      // Do not print the header if user cancelled the process, as we are going to redirect the user.
 113      echo $OUTPUT->header();
 114  }
 115  
 116  // Only let user perform a backup if we aren't in async mode, or if we are
 117  // and there are no pending backups for this item for this user.
 118  if (!async_helper::is_async_pending($id, 'course', 'backup')) {
 119  
 120      // The mix of business logic and display elements below makes me sad.
 121      // This needs to refactored into the renderer and seperated out.
 122  
 123      if (!($bc = backup_ui::load_controller($backupid))) {
 124          $bc = new backup_controller($type, $id, backup::FORMAT_MOODLE,
 125                  backup::INTERACTIVE_YES, $backupmode, $USER->id, backup::RELEASESESSION_YES);
 126          // The backup id did not relate to a valid controller so we made a new controller.
 127          // Now we need to reset the backup id to match the new controller.
 128          $backupid = $bc->get_backupid();
 129      }
 130  
 131      // Prepare a progress bar which can display optionally during long-running
 132      // operations while setting up the UI.
 133      $slowprogress = new \core\progress\display_if_slow(get_string('preparingui', 'backup'));
 134      $renderer = $PAGE->get_renderer('core', 'backup');
 135      $backup = new backup_ui($bc);
 136  
 137      if ($backup->get_stage() == backup_ui::STAGE_SCHEMA && !$previous) {
 138          // After schema stage, we are probably going to get to the confirmation stage,
 139          // The confirmation stage has 2 sets of progress, so this is needed to prevent
 140          // it showing 2 progress bars.
 141          $twobars = true;
 142          $slowprogress->start_progress('', 2);
 143      } else {
 144          $twobars = false;
 145      }
 146      $backup->get_controller()->set_progress($slowprogress);
 147      $backup->process();
 148  
 149      if ($backup->enforce_changed_dependencies()) {
 150          debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
 151      }
 152  
 153      $loghtml = '';
 154      if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
 155  
 156          // Before we perform the backup check settings to see if user
 157          // or setting defaults are set to exclude files from the backup.
 158          if ($backup->get_setting_value('files') == 0) {
 159              $renderer->set_samesite_notification();
 160          }
 161  
 162          if ($backupmode != backup::MODE_ASYNC) {
 163              // Synchronous backup handling.
 164  
 165              // Display an extra backup step bar so that we can show the 'processing' step first.
 166              echo html_writer::start_div('', array('id' => 'executionprogress'));
 167              echo $renderer->progress_bar($backup->get_progress_bar());
 168              $backup->get_controller()->set_progress(new \core\progress\display());
 169  
 170              // Prepare logger and add to end of chain.
 171              $logger = new core_backup_html_logger($CFG->debugdeveloper ? backup::LOG_DEBUG : backup::LOG_INFO);
 172              $backup->get_controller()->add_logger($logger);
 173  
 174              // Carry out actual backup.
 175              $backup->execute();
 176  
 177              // Backup controller gets saved/loaded so the logger object changes and we
 178              // have to retrieve it.
 179              $logger = $backup->get_controller()->get_logger();
 180              while (!is_a($logger, 'core_backup_html_logger')) {
 181                  $logger = $logger->get_next();
 182              }
 183  
 184              // Get HTML from logger.
 185              if ($CFG->debugdisplay) {
 186                  $loghtml = $logger->get_html();
 187              }
 188  
 189              // Hide the progress display and first backup step bar (the 'finished' step will show next).
 190              echo html_writer::end_div();
 191              echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
 192  
 193          } else {
 194              // Async backup handling.
 195              $backup->get_controller()->finish_ui();
 196  
 197              echo html_writer::start_div('', array('id' => 'executionprogress'));
 198              echo $renderer->progress_bar($backup->get_progress_bar());
 199              echo html_writer::end_div();
 200  
 201              // Create adhoc task for backup.
 202              $asynctask = new \core\task\asynchronous_backup_task();
 203              $asynctask->set_blocking(false);
 204              $asynctask->set_custom_data(array('backupid' => $backupid));
 205              \core\task\manager::queue_adhoc_task($asynctask);
 206  
 207              // Add ajax progress bar and initiate ajax via a template.
 208              $restoreurl = new moodle_url('/backup/restorefile.php', array('contextid' => $contextid));
 209              $progresssetup = array(
 210                      'backupid' => $backupid,
 211                      'contextid' => $contextid,
 212                      'courseurl' => $courseurl->out(),
 213                      'restoreurl' => $restoreurl->out(),
 214                      'headingident' => 'backup'
 215              );
 216  
 217              echo $renderer->render_from_template('core/async_backup_status', $progresssetup);
 218          }
 219  
 220      } else {
 221          $backup->save_controller();
 222      }
 223  
 224      if ($backup->get_stage() != backup_ui::STAGE_FINAL) {
 225  
 226          // Displaying UI can require progress reporting, so do it here before outputting
 227          // the backup stage bar (as part of the existing progress bar, if required).
 228          $ui = $backup->display($renderer);
 229          if ($twobars) {
 230              $slowprogress->end_progress();
 231          }
 232  
 233          echo $renderer->progress_bar($backup->get_progress_bar());
 234          echo $ui;
 235  
 236          // Display log data if there was any.
 237          if ($loghtml != '' && $backupmode != backup::MODE_ASYNC) {
 238              echo $renderer->log_display($loghtml);
 239          }
 240      }
 241  
 242      $backup->destroy();
 243      unset($backup);
 244  
 245  } else { // User has a pending async operation.
 246      echo $OUTPUT->notification(get_string('pendingasyncerror', 'backup'), 'error');
 247      echo $OUTPUT->container(get_string('pendingasyncdetail', 'backup'));
 248      echo $OUTPUT->continue_button($courseurl);
 249  }
 250  
 251  echo $OUTPUT->footer();