[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/backup/util/helper/ -> backup_helper.class.php (source)

   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   * @package    moodlecore
  20   * @subpackage backup-helper
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * Base abstract class for all the helper classes providing various operations
  27   *
  28   * TODO: Finish phpdocs
  29   */
  30  abstract class backup_helper {
  31  
  32      /**
  33       * Given one backupid, create all the needed dirs to have one backup temp dir available
  34       */
  35      static public function check_and_create_backup_dir($backupid) {
  36          global $CFG;
  37          if (!check_dir_exists($CFG->tempdir . '/backup/' . $backupid, true, true)) {
  38              throw new backup_helper_exception('cannot_create_backup_temp_dir');
  39          }
  40      }
  41  
  42      /**
  43       * Given one backupid, ensure its temp dir is completely empty
  44       *
  45       * If supplied, progress object should be ready to receive indeterminate
  46       * progress reports.
  47       *
  48       * @param string $backupid Backup id
  49       * @param \core\progress\base $progress Optional progress reporting object
  50       */
  51      static public function clear_backup_dir($backupid, \core\progress\base $progress = null) {
  52          global $CFG;
  53          if (!self::delete_dir_contents($CFG->tempdir . '/backup/' . $backupid, '', $progress)) {
  54              throw new backup_helper_exception('cannot_empty_backup_temp_dir');
  55          }
  56          return true;
  57      }
  58  
  59      /**
  60       * Given one backupid, delete completely its temp dir
  61       *
  62       * If supplied, progress object should be ready to receive indeterminate
  63       * progress reports.
  64       *
  65       * @param string $backupid Backup id
  66       * @param \core\progress\base $progress Optional progress reporting object
  67       */
  68       static public function delete_backup_dir($backupid, \core\progress\base $progress = null) {
  69           global $CFG;
  70           self::clear_backup_dir($backupid, $progress);
  71           return rmdir($CFG->tempdir . '/backup/' . $backupid);
  72       }
  73  
  74       /**
  75       * Given one fullpath to directory, delete its contents recursively
  76       * Copied originally from somewhere in the net.
  77       * TODO: Modernise this
  78       *
  79       * If supplied, progress object should be ready to receive indeterminate
  80       * progress reports.
  81       *
  82       * @param string $dir Directory to delete
  83       * @param string $excludedir Exclude this directory
  84       * @param \core\progress\base $progress Optional progress reporting object
  85       */
  86      static public function delete_dir_contents($dir, $excludeddir='', \core\progress\base $progress = null) {
  87          global $CFG;
  88  
  89          if ($progress) {
  90              $progress->progress();
  91          }
  92  
  93          if (!is_dir($dir)) {
  94              // if we've been given a directory that doesn't exist yet, return true.
  95              // this happens when we're trying to clear out a course that has only just
  96              // been created.
  97              return true;
  98          }
  99          $slash = "/";
 100  
 101          // Create arrays to store files and directories
 102          $dir_files      = array();
 103          $dir_subdirs    = array();
 104  
 105          // Make sure we can delete it
 106          chmod($dir, $CFG->directorypermissions);
 107  
 108          if ((($handle = opendir($dir))) == false) {
 109              // The directory could not be opened
 110              return false;
 111          }
 112  
 113          // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
 114          while (false !== ($entry = readdir($handle))) {
 115              if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) {
 116                  $dir_subdirs[] = $dir. $slash .$entry;
 117  
 118              } else if ($entry != ".." && $entry != "." && $entry != $excludeddir) {
 119                  $dir_files[] = $dir. $slash .$entry;
 120              }
 121          }
 122  
 123          // Delete all files in the curent directory return false and halt if a file cannot be removed
 124          for ($i=0; $i<count($dir_files); $i++) {
 125              chmod($dir_files[$i], $CFG->directorypermissions);
 126              if (((unlink($dir_files[$i]))) == false) {
 127                  return false;
 128              }
 129          }
 130  
 131          // Empty sub directories and then remove the directory
 132          for ($i=0; $i<count($dir_subdirs); $i++) {
 133              chmod($dir_subdirs[$i], $CFG->directorypermissions);
 134              if (self::delete_dir_contents($dir_subdirs[$i], '', $progress) == false) {
 135                  return false;
 136              } else {
 137                  if (remove_dir($dir_subdirs[$i]) == false) {
 138                      return false;
 139                  }
 140              }
 141          }
 142  
 143          // Close directory
 144          closedir($handle);
 145  
 146          // Success, every thing is gone return true
 147          return true;
 148      }
 149  
 150      /**
 151       * Delete all the temp dirs older than the time specified.
 152       *
 153       * If supplied, progress object should be ready to receive indeterminate
 154       * progress reports.
 155       *
 156       * @param int $deletefrom Time to delete from
 157       * @param \core\progress\base $progress Optional progress reporting object
 158       */
 159      static public function delete_old_backup_dirs($deletefrom, \core\progress\base $progress = null) {
 160          global $CFG;
 161  
 162          $status = true;
 163          // Get files and directories in the temp backup dir witout descend
 164          $list = get_directory_list($CFG->tempdir . '/backup', '', false, true, true);
 165          foreach ($list as $file) {
 166              $file_path = $CFG->tempdir . '/backup/' . $file;
 167              $moddate = filemtime($file_path);
 168              if ($status && $moddate < $deletefrom) {
 169                  //If directory, recurse
 170                  if (is_dir($file_path)) {
 171                      // $file is really the backupid
 172                      $status = self::delete_backup_dir($file, $progress);
 173                  //If file
 174                  } else {
 175                      unlink($file_path);
 176                  }
 177              }
 178          }
 179          if (!$status) {
 180              throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs');
 181          }
 182      }
 183  
 184      /**
 185       * This function will be invoked by any log() method in backup/restore, acting
 186       * as a simple forwarder to the standard loggers but also, if the $display
 187       * parameter is true, supporting translation via get_string() and sending to
 188       * standard output.
 189       */
 190      static public function log($message, $level, $a, $depth, $display, $logger) {
 191          // Send to standard loggers
 192          $logmessage = $message;
 193          $options = empty($depth) ? array() : array('depth' => $depth);
 194          if (!empty($a)) {
 195              $logmessage = $logmessage . ' ' . implode(', ', (array)$a);
 196          }
 197          $logger->process($logmessage, $level, $options);
 198  
 199          // If $display specified, send translated string to output_controller
 200          if ($display) {
 201              output_controller::get_instance()->output($message, 'backup', $a, $depth);
 202          }
 203      }
 204  
 205      /**
 206       * Given one backupid and the (FS) final generated file, perform its final storage
 207       * into Moodle file storage. For stored files it returns the complete file_info object
 208       *
 209       * Note: the $filepath is deleted if the backup file is created successfully
 210       *
 211       * If you specify the progress monitor, this will start a new progress section
 212       * to track progress in processing (in case this task takes a long time).
 213       *
 214       * @param int $backupid
 215       * @param string $filepath zip file containing the backup
 216       * @param \core\progress\base $progress Optional progress monitor
 217       * @return stored_file if created, null otherwise
 218       *
 219       * @throws moodle_exception in case of any problems
 220       */
 221      static public function store_backup_file($backupid, $filepath, \core\progress\base $progress = null) {
 222          global $CFG;
 223  
 224          // First of all, get some information from the backup_controller to help us decide
 225          list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information(
 226                  $backupid, $progress);
 227  
 228          // Extract useful information to decide
 229          $hasusers  = (bool)$sinfo['users']->value;     // Backup has users
 230          $isannon   = (bool)$sinfo['anonymize']->value; // Backup is anonymised
 231          $filename  = $sinfo['filename']->value;        // Backup filename
 232          $backupmode= $dinfo[0]->mode;                  // Backup mode backup::MODE_GENERAL/IMPORT/HUB
 233          $backuptype= $dinfo[0]->type;                  // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE
 234          $userid    = $dinfo[0]->userid;                // User->id executing the backup
 235          $id        = $dinfo[0]->id;                    // Id of activity/section/course (depends of type)
 236          $courseid  = $dinfo[0]->courseid;              // Id of the course
 237          $format    = $dinfo[0]->format;                // Type of backup file
 238  
 239          // Quick hack. If for any reason, filename is blank, fix it here.
 240          // TODO: This hack will be out once MDL-22142 - P26 gets fixed
 241          if (empty($filename)) {
 242              $filename = backup_plan_dbops::get_default_backup_filename('moodle2', $backuptype, $id, $hasusers, $isannon);
 243          }
 244  
 245          // Backups of type IMPORT aren't stored ever
 246          if ($backupmode == backup::MODE_IMPORT) {
 247              return null;
 248          }
 249  
 250          if (!is_readable($filepath)) {
 251              // we have a problem if zip file does not exist
 252              throw new coding_exception('backup_helper::store_backup_file() expects valid $filepath parameter');
 253  
 254          }
 255  
 256          // Calculate file storage options of id being backup
 257          $ctxid     = 0;
 258          $filearea  = '';
 259          $component = '';
 260          $itemid    = 0;
 261          switch ($backuptype) {
 262              case backup::TYPE_1ACTIVITY:
 263                  $ctxid     = context_module::instance($id)->id;
 264                  $component = 'backup';
 265                  $filearea  = 'activity';
 266                  $itemid    = 0;
 267                  break;
 268              case backup::TYPE_1SECTION:
 269                  $ctxid     = context_course::instance($courseid)->id;
 270                  $component = 'backup';
 271                  $filearea  = 'section';
 272                  $itemid    = $id;
 273                  break;
 274              case backup::TYPE_1COURSE:
 275                  $ctxid     = context_course::instance($courseid)->id;
 276                  $component = 'backup';
 277                  $filearea  = 'course';
 278                  $itemid    = 0;
 279                  break;
 280          }
 281  
 282          if ($backupmode == backup::MODE_AUTOMATED) {
 283              // Automated backups have there own special area!
 284              $filearea  = 'automated';
 285  
 286              // If we're keeping the backup only in a chosen path, just move it there now
 287              // this saves copying from filepool to here later and filling trashdir.
 288              $config = get_config('backup');
 289              $dir = $config->backup_auto_destination;
 290              if ($config->backup_auto_storage == 1 and $dir and is_dir($dir) and is_writable($dir)) {
 291                  $filedest = $dir.'/'.backup_plan_dbops::get_default_backup_filename($format, $backuptype, $courseid, $hasusers, $isannon, !$config->backup_shortname);
 292                  // first try to move the file, if it is not possible copy and delete instead
 293                  if (@rename($filepath, $filedest)) {
 294                      return null;
 295                  }
 296                  umask($CFG->umaskpermissions);
 297                  if (copy($filepath, $filedest)) {
 298                      @chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot
 299                      unlink($filepath);
 300                      return null;
 301                  } else {
 302                      $bc = backup_controller::load_controller($backupid);
 303                      $bc->log('Attempt to copy backup file to the specified directory using filesystem failed - ',
 304                              backup::LOG_WARNING, $dir);
 305                      $bc->destroy();
 306                  }
 307                  // bad luck, try to deal with the file the old way - keep backup in file area if we can not copy to ext system
 308              }
 309          }
 310  
 311          // Backups of type HUB (by definition never have user info)
 312          // are sent to user's "user_tohub" file area. The upload process
 313          // will be responsible for cleaning that filearea once finished
 314          if ($backupmode == backup::MODE_HUB) {
 315              $ctxid     = context_user::instance($userid)->id;
 316              $component = 'user';
 317              $filearea  = 'tohub';
 318              $itemid    = 0;
 319          }
 320  
 321          // Backups without user info or with the anonymise functionality
 322          // enabled are sent to user's "user_backup"
 323          // file area. Maintenance of such area is responsibility of
 324          // the user via corresponding file manager frontend
 325          if ($backupmode == backup::MODE_GENERAL && (!$hasusers || $isannon)) {
 326              $ctxid     = context_user::instance($userid)->id;
 327              $component = 'user';
 328              $filearea  = 'backup';
 329              $itemid    = 0;
 330          }
 331  
 332          // Let's send the file to file storage, everything already defined
 333          $fs = get_file_storage();
 334          $fr = array(
 335              'contextid'   => $ctxid,
 336              'component'   => $component,
 337              'filearea'    => $filearea,
 338              'itemid'      => $itemid,
 339              'filepath'    => '/',
 340              'filename'    => $filename,
 341              'userid'      => $userid,
 342              'timecreated' => time(),
 343              'timemodified'=> time());
 344          // If file already exists, delete if before
 345          // creating it again. This is BC behaviour - copy()
 346          // overwrites by default
 347          if ($fs->file_exists($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) {
 348              $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']);
 349              $sf = $fs->get_file_by_hash($pathnamehash);
 350              $sf->delete();
 351          }
 352          $file = $fs->create_file_from_pathname($fr, $filepath);
 353          unlink($filepath);
 354          return $file;
 355      }
 356  
 357      /**
 358       * This function simply marks one param to be considered as straight sql
 359       * param, so it won't be searched in the structure tree nor converted at
 360       * all. Useful for better integration of definition of sources in structure
 361       * and DB stuff
 362       */
 363      public static function is_sqlparam($value) {
 364          return array('sqlparam' => $value);
 365      }
 366  
 367      /**
 368       * This function returns one array of itemnames that are being handled by
 369       * inforef.xml files. Used both by backup and restore
 370       */
 371      public static function get_inforef_itemnames() {
 372          return array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item', 'question_category');
 373      }
 374  }
 375  
 376  /*
 377   * Exception class used by all the @helper stuff
 378   */
 379  class backup_helper_exception extends backup_exception {
 380  
 381      public function __construct($errorcode, $a=NULL, $debuginfo=null) {
 382          parent::__construct($errorcode, $a, $debuginfo);
 383      }
 384  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1