[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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-controller 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 * Class implementing the controller of any restore process 27 * 28 * This final class is in charge of controlling all the restore architecture, for any 29 * type of backup. 30 * 31 * TODO: Finish phpdocs 32 */ 33 class restore_controller extends base_controller { 34 35 protected $tempdir; // Directory under tempdir/backup awaiting restore 36 protected $restoreid; // Unique identificator for this restore 37 38 protected $courseid; // courseid where restore is going to happen 39 40 protected $type; // Type of backup (activity, section, course) 41 protected $format; // Format of backup (moodle, imscc) 42 protected $interactive; // yes/no 43 protected $mode; // Purpose of the backup (default settings) 44 protected $userid; // user id executing the restore 45 protected $operation; // Type of operation (backup/restore) 46 protected $target; // Restoring to new/existing/current_adding/_deleting 47 protected $samesite; // Are we restoring to the same site where the backup was generated 48 49 protected $status; // Current status of the controller (created, planned, configured...) 50 protected $precheck; // Results of the execution of restore prechecks 51 52 protected $info; // Information retrieved from backup contents 53 protected $plan; // Restore execution plan 54 55 protected $execution; // inmediate/delayed 56 protected $executiontime; // epoch time when we want the restore to be executed (requires cron to run) 57 58 protected $checksum; // Cache @checksumable results for lighter @is_checksum_correct() uses 59 60 /** @var int Number of restore_controllers that are currently executing */ 61 protected static $executing = 0; 62 63 /** 64 * Constructor. 65 * 66 * If you specify a progress monitor, this will be used to report progress 67 * while loading the plan, as well as for future use. (You can change it 68 * for a different one later using set_progress.) 69 * 70 * @param string $tempdir Directory under tempdir/backup awaiting restore 71 * @param int $courseid Course id where restore is going to happen 72 * @param bool $interactive backup::INTERACTIVE_YES[true] or backup::INTERACTIVE_NO[false] 73 * @param int $mode backup::MODE_[ GENERAL | HUB | IMPORT | SAMESITE ] 74 * @param int $userid 75 * @param int $target backup::TARGET_[ NEW_COURSE | CURRENT_ADDING | CURRENT_DELETING | EXISTING_ADDING | EXISTING_DELETING ] 76 * @param \core\progress\base $progress Optional progress monitor 77 */ 78 public function __construct($tempdir, $courseid, $interactive, $mode, $userid, $target, 79 \core\progress\base $progress = null) { 80 $this->tempdir = $tempdir; 81 $this->courseid = $courseid; 82 $this->interactive = $interactive; 83 $this->mode = $mode; 84 $this->userid = $userid; 85 $this->target = $target; 86 87 // Apply some defaults 88 $this->type = ''; 89 $this->format = backup::FORMAT_UNKNOWN; 90 $this->execution = backup::EXECUTION_INMEDIATE; 91 $this->operation = backup::OPERATION_RESTORE; 92 $this->executiontime = 0; 93 $this->samesite = false; 94 $this->checksum = ''; 95 $this->precheck = null; 96 97 // Apply current backup version and release if necessary 98 backup_controller_dbops::apply_version_and_release(); 99 100 // Check courseid is correct 101 restore_check::check_courseid($this->courseid); 102 103 // Check user is correct 104 restore_check::check_user($this->userid); 105 106 // Calculate unique $restoreid 107 $this->calculate_restoreid(); 108 109 // Default logger chain (based on interactive/execution) 110 $this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->restoreid); 111 112 // By default there is no progress reporter unless you specify one so it 113 // can be used during loading of the plan. 114 if ($progress) { 115 $this->progress = $progress; 116 } else { 117 $this->progress = new \core\progress\null(); 118 } 119 $this->progress->start_progress('Constructing restore_controller'); 120 121 // Instantiate the output_controller singleton and active it if interactive and inmediate 122 $oc = output_controller::get_instance(); 123 if ($this->interactive == backup::INTERACTIVE_YES && $this->execution == backup::EXECUTION_INMEDIATE) { 124 $oc->set_active(true); 125 } 126 127 $this->log('instantiating restore controller', backup::LOG_INFO, $this->restoreid); 128 129 // Set initial status 130 $this->set_status(backup::STATUS_CREATED); 131 132 // Calculate original restore format 133 $this->format = backup_general_helper::detect_backup_format($tempdir); 134 135 // If format is not moodle2, set to conversion needed 136 if ($this->format !== backup::FORMAT_MOODLE) { 137 $this->set_status(backup::STATUS_REQUIRE_CONV); 138 139 // Else, format is moodle2, load plan, apply security and set status based on interactivity 140 } else { 141 // Load plan 142 $this->load_plan(); 143 144 // Perform all initial security checks and apply (2nd param) them to settings automatically 145 restore_check::check_security($this, true); 146 147 if ($this->interactive == backup::INTERACTIVE_YES) { 148 $this->set_status(backup::STATUS_SETTING_UI); 149 } else { 150 $this->set_status(backup::STATUS_NEED_PRECHECK); 151 } 152 } 153 154 // Tell progress monitor that we finished loading. 155 $this->progress->end_progress(); 156 } 157 158 /** 159 * Clean structures used by the restore_controller 160 * 161 * This method clean various structures used by the restore_controller, 162 * destroying them in an ordered way, so their memory will be gc properly 163 * by PHP (mainly circular references). 164 * 165 * Note that, while it's not mandatory to execute this method, it's highly 166 * recommended to do so, specially in scripts performing multiple operations 167 * (like the automated backups) or the system will run out of memory after 168 * a few dozens of backups) 169 */ 170 public function destroy() { 171 // Only need to destroy circulars under the plan. Delegate to it. 172 $this->plan->destroy(); 173 } 174 175 public function finish_ui() { 176 if ($this->status != backup::STATUS_SETTING_UI) { 177 throw new restore_controller_exception('cannot_finish_ui_if_not_setting_ui'); 178 } 179 $this->set_status(backup::STATUS_NEED_PRECHECK); 180 } 181 182 public function process_ui_event() { 183 184 // Perform security checks throwing exceptions (2nd param) if something is wrong 185 restore_check::check_security($this, false); 186 } 187 188 public function set_status($status) { 189 // Note: never save_controller() with the object info after STATUS_EXECUTING or the whole controller, 190 // containing all the steps will be sent to DB. 100% (monster) useless. 191 $this->log('setting controller status to', backup::LOG_DEBUG, $status); 192 // TODO: Check it's a correct status. 193 $this->status = $status; 194 // Ensure that, once set to backup::STATUS_AWAITING | STATUS_NEED_PRECHECK, controller is stored in DB. 195 if ($status == backup::STATUS_AWAITING || $status == backup::STATUS_NEED_PRECHECK) { 196 $this->save_controller(); 197 $tbc = self::load_controller($this->restoreid); 198 $this->logger = $tbc->logger; // wakeup loggers 199 $tbc->destroy(); // Clean temp controller structures 200 201 } else if ($status == backup::STATUS_FINISHED_OK) { 202 // If the operation has ended without error (backup::STATUS_FINISHED_OK) 203 // proceed by cleaning the object from database. MDL-29262. 204 $this->save_controller(false, true); 205 } 206 } 207 208 public function set_execution($execution, $executiontime = 0) { 209 $this->log('setting controller execution', backup::LOG_DEBUG); 210 // TODO: Check valid execution mode 211 // TODO: Check time in future 212 // TODO: Check time = 0 if inmediate 213 $this->execution = $execution; 214 $this->executiontime = $executiontime; 215 216 // Default logger chain (based on interactive/execution) 217 $this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->restoreid); 218 } 219 220 // checksumable interface methods 221 222 public function calculate_checksum() { 223 // Reset current checksum to take it out from calculations! 224 $this->checksum = ''; 225 // Init checksum 226 $tempchecksum = md5('tempdir-' . $this->tempdir . 227 'restoreid-' . $this->restoreid . 228 'courseid-' . $this->courseid . 229 'type-' . $this->type . 230 'format-' . $this->format . 231 'interactive-'. $this->interactive . 232 'mode-' . $this->mode . 233 'userid-' . $this->userid . 234 'target-' . $this->target . 235 'samesite-' . $this->samesite . 236 'operation-' . $this->operation . 237 'status-' . $this->status . 238 'precheck-' . backup_general_helper::array_checksum_recursive(array($this->precheck)) . 239 'execution-' . $this->execution . 240 'plan-' . backup_general_helper::array_checksum_recursive(array($this->plan)) . 241 'info-' . backup_general_helper::array_checksum_recursive(array($this->info)) . 242 'logger-' . backup_general_helper::array_checksum_recursive(array($this->logger))); 243 $this->log('calculating controller checksum', backup::LOG_DEBUG, $tempchecksum); 244 return $tempchecksum; 245 } 246 247 public function is_checksum_correct($checksum) { 248 return $this->checksum === $checksum; 249 } 250 251 public function get_tempdir() { 252 return $this->tempdir; 253 } 254 255 public function get_restoreid() { 256 return $this->restoreid; 257 } 258 259 public function get_type() { 260 return $this->type; 261 } 262 263 public function get_operation() { 264 return $this->operation; 265 } 266 267 public function get_courseid() { 268 return $this->courseid; 269 } 270 271 public function get_format() { 272 return $this->format; 273 } 274 275 public function get_interactive() { 276 return $this->interactive; 277 } 278 279 public function get_mode() { 280 return $this->mode; 281 } 282 283 public function get_userid() { 284 return $this->userid; 285 } 286 287 public function get_target() { 288 return $this->target; 289 } 290 291 public function is_samesite() { 292 return $this->samesite; 293 } 294 295 public function get_status() { 296 return $this->status; 297 } 298 299 public function get_execution() { 300 return $this->execution; 301 } 302 303 public function get_executiontime() { 304 return $this->executiontime; 305 } 306 307 /** 308 * Returns the restore plan 309 * @return restore_plan 310 */ 311 public function get_plan() { 312 return $this->plan; 313 } 314 315 public function get_info() { 316 return $this->info; 317 } 318 319 public function execute_plan() { 320 // Basic/initial prevention against time/memory limits 321 core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted 322 raise_memory_limit(MEMORY_EXTRA); 323 // If this is not a course restore, inform the plan we are not 324 // including all the activities for sure. This will affect any 325 // task/step executed conditionally to stop processing information 326 // for section and activity restore. MDL-28180. 327 if ($this->get_type() !== backup::TYPE_1COURSE) { 328 $this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG); 329 $this->plan->set_excluding_activities(); 330 } 331 self::$executing++; 332 try { 333 $this->plan->execute(); 334 } catch (Exception $e) { 335 self::$executing--; 336 throw $e; 337 } 338 self::$executing--; 339 } 340 341 /** 342 * Checks whether restore is currently executing. Certain parts of code that 343 * is called during restore, but not directly part of the restore system, may 344 * need to behave differently during restore (e.g. do not bother resetting a 345 * cache because we know it will be reset at end of operation). 346 * 347 * @return bool True if any restore is currently executing 348 */ 349 public static function is_executing() { 350 return self::$executing > 0; 351 } 352 353 /** 354 * Execute the restore prechecks to detect any problem before proceed with restore 355 * 356 * This function checks various parts of the restore (versions, users, roles...) 357 * returning true if everything was ok or false if any warning/error was detected. 358 * Any warning/error is returned by the get_precheck_results() method. 359 * Note: if any problem is found it will, automatically, drop all the restore temp 360 * tables as far as the next step is to inform about the warning/errors. If no problem 361 * is found, then default behaviour is to keep the temp tables so, in the same request 362 * restore will be executed, saving a lot of checks to be executed again. 363 * Note: If for any reason (UI to show after prechecks...) you want to force temp tables 364 * to be dropped always, you can pass true to the $droptemptablesafter parameter 365 */ 366 public function execute_precheck($droptemptablesafter = false) { 367 if (is_array($this->precheck)) { 368 throw new restore_controller_exception('precheck_alredy_executed', $this->status); 369 } 370 if ($this->status != backup::STATUS_NEED_PRECHECK) { 371 throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status); 372 } 373 // Basic/initial prevention against time/memory limits 374 core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted 375 raise_memory_limit(MEMORY_EXTRA); 376 $this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter); 377 if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed 378 $this->set_status(backup::STATUS_AWAITING); 379 } 380 if (empty($this->precheck)) { // No errors nor warnings, return true 381 return true; 382 } 383 return false; 384 } 385 386 public function get_results() { 387 return $this->plan->get_results(); 388 } 389 390 /** 391 * Returns true if the prechecks have been executed 392 * @return bool 393 */ 394 public function precheck_executed() { 395 return (is_array($this->precheck)); 396 } 397 398 public function get_precheck_results() { 399 if (!is_array($this->precheck)) { 400 throw new restore_controller_exception('precheck_not_executed'); 401 } 402 return $this->precheck; 403 } 404 405 /** 406 * Save controller information 407 * 408 * @param bool $includeobj to decide if the object itself must be updated (true) or no (false) 409 * @param bool $cleanobj to decide if the object itself must be cleaned (true) or no (false) 410 */ 411 public function save_controller($includeobj = true, $cleanobj = false) { 412 // Going to save controller to persistent storage, calculate checksum for later checks and save it 413 // TODO: flag the controller as NA. Any operation on it should be forbidden util loaded back 414 $this->log('saving controller to db', backup::LOG_DEBUG); 415 if ($includeobj ) { // Only calculate checksum if we are going to include the object. 416 $this->checksum = $this->calculate_checksum(); 417 } 418 restore_controller_dbops::save_controller($this, $this->checksum, $includeobj, $cleanobj); 419 } 420 421 public static function load_controller($restoreid) { 422 // Load controller from persistent storage 423 // TODO: flag the controller as available. Operations on it can continue 424 $controller = restore_controller_dbops::load_controller($restoreid); 425 $controller->log('loading controller from db', backup::LOG_DEBUG); 426 return $controller; 427 } 428 429 /** 430 * class method to provide pseudo random unique "correct" tempdir names 431 */ 432 public static function get_tempdir_name($courseid = 0, $userid = 0) { 433 // Current epoch time + courseid + userid + random bits 434 return md5(time() . '-' . $courseid . '-'. $userid . '-'. random_string(20)); 435 } 436 437 /** 438 * Converts from current format to backup::MOODLE format 439 */ 440 public function convert() { 441 global $CFG; 442 require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php'); 443 444 // Basic/initial prevention against time/memory limits 445 core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted 446 raise_memory_limit(MEMORY_EXTRA); 447 $this->progress->start_progress('Backup format conversion'); 448 449 if ($this->status != backup::STATUS_REQUIRE_CONV) { 450 throw new restore_controller_exception('cannot_convert_not_required_status'); 451 } 452 453 $this->log('backup format conversion required', backup::LOG_INFO); 454 455 // Run conversion to the proper format 456 if (!convert_helper::to_moodle2_format($this->get_tempdir(), $this->format, $this->get_logger())) { 457 // todo - unable to find the conversion path, what to do now? 458 // throwing the exception as a temporary solution 459 throw new restore_controller_exception('unable_to_find_conversion_path'); 460 } 461 462 $this->log('backup format conversion successful', backup::LOG_INFO); 463 464 // If no exceptions were thrown, then we are in the proper format 465 $this->format = backup::FORMAT_MOODLE; 466 467 // Load plan, apply security and set status based on interactivity 468 $this->load_plan(); 469 470 // Perform all initial security checks and apply (2nd param) them to settings automatically 471 restore_check::check_security($this, true); 472 473 if ($this->interactive == backup::INTERACTIVE_YES) { 474 $this->set_status(backup::STATUS_SETTING_UI); 475 } else { 476 $this->set_status(backup::STATUS_NEED_PRECHECK); 477 } 478 $this->progress->end_progress(); 479 } 480 481 // Protected API starts here 482 483 protected function calculate_restoreid() { 484 // Current epoch time + tempdir + courseid + interactive + mode + userid + target + operation + random bits 485 $this->restoreid = md5(time() . '-' . $this->tempdir . '-' . $this->courseid . '-'. $this->interactive . '-' . 486 $this->mode . '-' . $this->userid . '-'. $this->target . '-' . $this->operation . '-' . 487 random_string(20)); 488 } 489 490 protected function load_plan() { 491 // First of all, we need to introspect the moodle_backup.xml file 492 // in order to detect all the required stuff. So, create the 493 // monster $info structure where everything will be defined 494 $this->log('loading backup info', backup::LOG_DEBUG); 495 $this->info = backup_general_helper::get_backup_information($this->tempdir); 496 497 // Set the controller type to the one found in the information 498 $this->type = $this->info->type; 499 500 // Set the controller samesite flag as needed 501 $this->samesite = backup_general_helper::backup_is_samesite($this->info); 502 503 // Now we load the plan that will be configured following the 504 // information provided by the $info 505 $this->log('loading controller plan', backup::LOG_DEBUG); 506 $this->plan = new restore_plan($this); 507 $this->plan->build(); // Build plan for this controller 508 $this->set_status(backup::STATUS_PLANNED); 509 } 510 } 511 512 /* 513 * Exception class used by all the @restore_controller stuff 514 */ 515 class restore_controller_exception extends backup_exception { 516 517 public function __construct($errorcode, $a=NULL, $debuginfo=null) { 518 parent::__construct($errorcode, $a, $debuginfo); 519 } 520 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |