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   * Provides base converter classes
  20   *
  21   * @package    core
  22   * @subpackage backup-convert
  23   * @copyright  2011 Mark Nielsen <mark@moodlerooms.com>
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  require_once($CFG->dirroot . '/backup/util/includes/convert_includes.php');
  30  
  31  /**
  32   * Base converter class
  33   *
  34   * All Moodle backup converters are supposed to extend this base class.
  35   *
  36   * @throws convert_exception
  37   */
  38  abstract class base_converter implements loggable {
  39  
  40      /** @var string unique identifier of this converter instance */
  41      protected $id;
  42  
  43      /** @var string the name of the directory containing the unpacked backup being converted */
  44      protected $tempdir;
  45  
  46      /** @var string the name of the directory where the backup is converted to */
  47      protected $workdir;
  48  
  49      /** @var null|base_logger logger to use during the conversion */
  50      protected $logger = null;
  51  
  52      /**
  53       * Constructor
  54       *
  55       * @param string $tempdir the relative path to the directory containing the unpacked backup to convert
  56       * @param null|base_logger logger to use during the conversion
  57       */
  58      public function __construct($tempdir, $logger = null) {
  59  
  60          $this->tempdir  = $tempdir;
  61          $this->id       = convert_helper::generate_id($tempdir);
  62          $this->workdir  = $tempdir . '_' . $this->get_name() . '_' . $this->id;
  63          $this->set_logger($logger);
  64          $this->log('instantiating '.$this->get_name().' converter '.$this->get_id(), backup::LOG_DEBUG);
  65          $this->log('conversion source directory', backup::LOG_DEBUG, $this->tempdir);
  66          $this->log('conversion target directory', backup::LOG_DEBUG, $this->workdir);
  67          $this->init();
  68      }
  69  
  70      /**
  71       * Sets the logger to use during the conversion
  72       *
  73       * @param null|base_logger $logger
  74       */
  75      public function set_logger($logger) {
  76          if (is_null($logger) or ($logger instanceof base_logger)) {
  77              $this->logger = $logger;
  78          }
  79      }
  80  
  81      /**
  82       * If the logger was set for the converter, log the message
  83       *
  84       * If the $display is enabled, the spaces in the $message text are removed
  85       * and the text is used as a string identifier in the core_backup language file.
  86       *
  87       * @see backup_helper::log()
  88       * @param string $message message text
  89       * @param int $level message level {@example backup::LOG_WARNING}
  90       * @param null|mixed $a additional information
  91       * @param null|int $depth the message depth
  92       * @param bool $display whether the message should be sent to the output, too
  93       */
  94      public function log($message, $level, $a = null, $depth = null, $display = false) {
  95          if ($this->logger instanceof base_logger) {
  96              backup_helper::log($message, $level, $a, $depth, $display, $this->logger);
  97          }
  98      }
  99  
 100      /**
 101       * Get instance identifier
 102       *
 103       * @return string the unique identifier of this converter instance
 104       */
 105      public function get_id() {
 106          return $this->id;
 107      }
 108  
 109      /**
 110       * Get converter name
 111       *
 112       * @return string the system name of the converter
 113       */
 114      public function get_name() {
 115          $parts = explode('_', get_class($this));
 116          return array_shift($parts);
 117      }
 118  
 119      /**
 120       * Converts the backup directory
 121       */
 122      public function convert() {
 123  
 124          try {
 125              $this->log('creating the target directory', backup::LOG_DEBUG);
 126              $this->create_workdir();
 127  
 128              $this->log('executing the conversion', backup::LOG_DEBUG);
 129              $this->execute();
 130  
 131              $this->log('replacing the source directory with the converted version', backup::LOG_DEBUG);
 132              $this->replace_tempdir();
 133          } catch (Exception $e) {
 134          }
 135  
 136          // clean-up stuff if needed
 137          $this->destroy();
 138  
 139          // eventually re-throw the execution exception
 140          if (isset($e) and ($e instanceof Exception)) {
 141              throw $e;
 142          }
 143      }
 144  
 145      /**
 146       * @return string the full path to the working directory
 147       */
 148      public function get_workdir_path() {
 149          global $CFG;
 150  
 151          return make_backup_temp_directory($this->workdir);
 152      }
 153  
 154      /**
 155       * @return string the full path to the directory with the source backup
 156       */
 157      public function get_tempdir_path() {
 158          global $CFG;
 159  
 160          return make_backup_temp_directory($this->tempdir);
 161      }
 162  
 163      /// public static methods //////////////////////////////////////////////////
 164  
 165      /**
 166       * Makes sure that this converter is available at this site
 167       *
 168       * This is intended for eventual PHP extensions check, environment check etc.
 169       * All checks that do not depend on actual backup data should be done here.
 170       *
 171       * @return boolean true if this converter should be considered as available
 172       */
 173      public static function is_available() {
 174          return true;
 175      }
 176  
 177      /**
 178       * Detects the format of the backup directory
 179       *
 180       * Moodle 2.x format is being detected by the core itself. The converters are
 181       * therefore supposed to detect the source format. Eventually, if the target
 182       * format os not {@link backup::FORMAT_MOODLE} then they should be able to
 183       * detect both source and target formats.
 184       *
 185       * @param string $tempdir the name of the backup directory
 186       * @return null|string null if not recognized, backup::FORMAT_xxx otherwise
 187       */
 188      public static function detect_format($tempdir) {
 189          return null;
 190      }
 191  
 192      /**
 193       * Returns the basic information about the converter
 194       *
 195       * The returned array must contain the following keys:
 196       * 'from' - the supported source format, eg. backup::FORMAT_MOODLE1
 197       * 'to'   - the supported target format, eg. backup::FORMAT_MOODLE
 198       * 'cost' - the cost of the conversion, non-negative non-zero integer
 199       */
 200      public static function description() {
 201  
 202          return array(
 203              'from'  => null,
 204              'to'    => null,
 205              'cost'  => null,
 206          );
 207      }
 208  
 209      /// end of public API //////////////////////////////////////////////////////
 210  
 211      /**
 212       * Initialize the instance if needed, called by the constructor
 213       */
 214      protected function init() {
 215      }
 216  
 217      /**
 218       * Converts the contents of the tempdir into the target format in the workdir
 219       */
 220      protected abstract function execute();
 221  
 222      /**
 223       * Prepares a new empty working directory
 224       */
 225      protected function create_workdir() {
 226  
 227          fulldelete($this->get_workdir_path());
 228          if (!check_dir_exists($this->get_workdir_path())) {
 229              throw new convert_exception('failed_create_workdir');
 230          }
 231      }
 232  
 233      /**
 234       * Replaces the source backup directory with the converted version
 235       *
 236       * If $CFG->keeptempdirectoriesonbackup is defined, the original source
 237       * source backup directory is kept for debugging purposes.
 238       */
 239      protected function replace_tempdir() {
 240          global $CFG;
 241  
 242          $tempdir = $this->get_tempdir_path();
 243  
 244          if (empty($CFG->keeptempdirectoriesonbackup)) {
 245              fulldelete($tempdir);
 246          } else {
 247              if (!rename($tempdir, $tempdir . '_' . $this->get_name() . '_' . $this->id . '_source')) {
 248                  throw new convert_exception('failed_rename_source_tempdir');
 249              }
 250          }
 251  
 252          if (!rename($this->get_workdir_path(), $tempdir)) {
 253              throw new convert_exception('failed_move_converted_into_place');
 254          }
 255      }
 256  
 257      /**
 258       * Cleans up stuff after the execution
 259       *
 260       * Note that we do not know if the execution was successful or not.
 261       * An exception might have been thrown.
 262       */
 263      protected function destroy() {
 264          global $CFG;
 265  
 266          if (empty($CFG->keeptempdirectoriesonbackup)) {
 267              fulldelete($this->get_workdir_path());
 268          }
 269      }
 270  }
 271  
 272  /**
 273   * General convert-related exception
 274   *
 275   * @author David Mudrak <david@moodle.com>
 276   */
 277  class convert_exception extends moodle_exception {
 278  
 279      /**
 280       * Constructor
 281       *
 282       * @param string $errorcode key for the corresponding error string
 283       * @param object $a extra words and phrases that might be required in the error string
 284       * @param string $debuginfo optional debugging information
 285       */
 286      public function __construct($errorcode, $a = null, $debuginfo = null) {
 287          parent::__construct($errorcode, '', '', $a, $debuginfo);
 288      }
 289  }