See Release Notes
Long Term Support Release
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 * @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 $backupiddir = make_backup_temp_directory($backupid, false); 37 if (empty($backupiddir)) { 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 $backupiddir = make_backup_temp_directory($backupid, false); 53 if (!self::delete_dir_contents($backupiddir, '', $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 $backupiddir = make_backup_temp_directory($backupid, false); 70 self::clear_backup_dir($backupid, $progress); 71 return rmdir($backupiddir); 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 $deletebefore Delete files and directories older than this time 157 * @param \core\progress\base $progress Optional progress reporting object 158 */ 159 static public function delete_old_backup_dirs($deletebefore, \core\progress\base $progress = null) { 160 $status = true; 161 // Get files and directories in the backup temp dir. 162 $backuptempdir = make_backup_temp_directory(''); 163 $items = new DirectoryIterator($backuptempdir); 164 foreach ($items as $item) { 165 if ($item->isDot()) { 166 continue; 167 } 168 if ($item->getMTime() < $deletebefore) { 169 if ($item->isDir()) { 170 // The item is a directory for some backup. 171 if (!self::delete_backup_dir($item->getFilename(), $progress)) { 172 // Something went wrong. Finish the list of items and then throw an exception. 173 $status = false; 174 } 175 } else if ($item->isFile()) { 176 unlink($item->getPathname()); 177 } 178 } 179 } 180 if (!$status) { 181 throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs'); 182 } 183 } 184 185 /** 186 * This function will be invoked by any log() method in backup/restore, acting 187 * as a simple forwarder to the standard loggers but also, if the $display 188 * parameter is true, supporting translation via get_string() and sending to 189 * standard output. 190 */ 191 static public function log($message, $level, $a, $depth, $display, $logger) { 192 // Send to standard loggers 193 $logmessage = $message; 194 $options = empty($depth) ? array() : array('depth' => $depth); 195 if (!empty($a)) { 196 $logmessage = $logmessage . ' ' . implode(', ', (array)$a); 197 } 198 $logger->process($logmessage, $level, $options); 199 200 // If $display specified, send translated string to output_controller 201 if ($display) { 202 output_controller::get_instance()->output($message, 'backup', $a, $depth); 203 } 204 } 205 206 /** 207 * Given one backupid and the (FS) final generated file, perform its final storage 208 * into Moodle file storage. For stored files it returns the complete file_info object 209 * 210 * Note: the $filepath is deleted if the backup file is created successfully 211 * 212 * If you specify the progress monitor, this will start a new progress section 213 * to track progress in processing (in case this task takes a long time). 214 * 215 * @param int $backupid 216 * @param string $filepath zip file containing the backup 217 * @param \core\progress\base $progress Optional progress monitor 218 * @return stored_file if created, null otherwise 219 * 220 * @throws moodle_exception in case of any problems 221 */ 222 static public function store_backup_file($backupid, $filepath, \core\progress\base $progress = null) { 223 global $CFG; 224 225 // First of all, get some information from the backup_controller to help us decide 226 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information( 227 $backupid, $progress); 228 229 // Extract useful information to decide 230 $hasusers = (bool)$sinfo['users']->value; // Backup has users 231 $isannon = (bool)$sinfo['anonymize']->value; // Backup is anonymised 232 $filename = $sinfo['filename']->value; // Backup filename 233 $backupmode= $dinfo[0]->mode; // Backup mode backup::MODE_GENERAL/IMPORT/HUB 234 $backuptype= $dinfo[0]->type; // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE 235 $userid = $dinfo[0]->userid; // User->id executing the backup 236 $id = $dinfo[0]->id; // Id of activity/section/course (depends of type) 237 $courseid = $dinfo[0]->courseid; // Id of the course 238 $format = $dinfo[0]->format; // Type of backup file 239 240 // Quick hack. If for any reason, filename is blank, fix it here. 241 // TODO: This hack will be out once MDL-22142 - P26 gets fixed 242 if (empty($filename)) { 243 $filename = backup_plan_dbops::get_default_backup_filename('moodle2', $backuptype, $id, $hasusers, $isannon); 244 } 245 246 // Backups of type IMPORT aren't stored ever 247 if ($backupmode == backup::MODE_IMPORT) { 248 return null; 249 } 250 251 if (!is_readable($filepath)) { 252 // we have a problem if zip file does not exist 253 throw new coding_exception('backup_helper::store_backup_file() expects valid $filepath parameter'); 254 255 } 256 257 // Calculate file storage options of id being backup 258 $ctxid = 0; 259 $filearea = ''; 260 $component = ''; 261 $itemid = 0; 262 switch ($backuptype) { 263 case backup::TYPE_1ACTIVITY: 264 $ctxid = context_module::instance($id)->id; 265 $component = 'backup'; 266 $filearea = 'activity'; 267 $itemid = 0; 268 break; 269 case backup::TYPE_1SECTION: 270 $ctxid = context_course::instance($courseid)->id; 271 $component = 'backup'; 272 $filearea = 'section'; 273 $itemid = $id; 274 break; 275 case backup::TYPE_1COURSE: 276 $ctxid = context_course::instance($courseid)->id; 277 $component = 'backup'; 278 $filearea = 'course'; 279 $itemid = 0; 280 break; 281 } 282 283 if ($backupmode == backup::MODE_AUTOMATED) { 284 // Automated backups have there own special area! 285 $filearea = 'automated'; 286 287 // If we're keeping the backup only in a chosen path, just move it there now 288 // this saves copying from filepool to here later and filling trashdir. 289 $config = get_config('backup'); 290 $dir = $config->backup_auto_destination; 291 if ($config->backup_auto_storage == 1 and $dir and is_dir($dir) and is_writable($dir)) { 292 $filedest = $dir.'/' 293 .backup_plan_dbops::get_default_backup_filename( 294 $format, 295 $backuptype, 296 $courseid, 297 $hasusers, 298 $isannon, 299 !$config->backup_shortname, 300 (bool)$config->backup_auto_files); 301 // first try to move the file, if it is not possible copy and delete instead 302 if (@rename($filepath, $filedest)) { 303 return null; 304 } 305 umask($CFG->umaskpermissions); 306 if (copy($filepath, $filedest)) { 307 @chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot 308 unlink($filepath); 309 return null; 310 } else { 311 $bc = backup_controller::load_controller($backupid); 312 $bc->log('Attempt to copy backup file to the specified directory using filesystem failed - ', 313 backup::LOG_WARNING, $dir); 314 $bc->destroy(); 315 } 316 // bad luck, try to deal with the file the old way - keep backup in file area if we can not copy to ext system 317 } 318 } 319 320 // Backups of type HUB (by definition never have user info) 321 // are sent to user's "user_tohub" file area. The upload process 322 // will be responsible for cleaning that filearea once finished 323 if ($backupmode == backup::MODE_HUB) { 324 $ctxid = context_user::instance($userid)->id; 325 $component = 'user'; 326 $filearea = 'tohub'; 327 $itemid = 0; 328 } 329 330 // Backups without user info or with the anonymise functionality 331 // enabled are sent to user's "user_backup" 332 // file area. Maintenance of such area is responsibility of 333 // the user via corresponding file manager frontend 334 if (($backupmode == backup::MODE_GENERAL || $backupmode == backup::MODE_ASYNC) && (!$hasusers || $isannon)) { 335 $ctxid = context_user::instance($userid)->id; 336 $component = 'user'; 337 $filearea = 'backup'; 338 $itemid = 0; 339 } 340 341 // Let's send the file to file storage, everything already defined 342 $fs = get_file_storage(); 343 $fr = array( 344 'contextid' => $ctxid, 345 'component' => $component, 346 'filearea' => $filearea, 347 'itemid' => $itemid, 348 'filepath' => '/', 349 'filename' => $filename, 350 'userid' => $userid, 351 'timecreated' => time(), 352 'timemodified'=> time()); 353 // If file already exists, delete if before 354 // creating it again. This is BC behaviour - copy() 355 // overwrites by default 356 if ($fs->file_exists($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) { 357 $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']); 358 $sf = $fs->get_file_by_hash($pathnamehash); 359 $sf->delete(); 360 } 361 $file = $fs->create_file_from_pathname($fr, $filepath); 362 unlink($filepath); 363 return $file; 364 } 365 366 /** 367 * This function simply marks one param to be considered as straight sql 368 * param, so it won't be searched in the structure tree nor converted at 369 * all. Useful for better integration of definition of sources in structure 370 * and DB stuff 371 */ 372 public static function is_sqlparam($value) { 373 return array('sqlparam' => $value); 374 } 375 376 /** 377 * This function returns one array of itemnames that are being handled by 378 * inforef.xml files. Used both by backup and restore 379 */ 380 public static function get_inforef_itemnames() { 381 return array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item', 'question_category'); 382 } 383 } 384 385 /* 386 * Exception class used by all the @helper stuff 387 */ 388 class backup_helper_exception extends backup_exception { 389 390 public function __construct($errorcode, $a=NULL, $debuginfo=null) { 391 parent::__construct($errorcode, $a, $debuginfo); 392 } 393 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body