[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * File containing the course class. 19 * 20 * @package tool_uploadcourse 21 * @copyright 2013 Frédéric Massart 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 27 require_once($CFG->dirroot . '/course/lib.php'); 28 29 /** 30 * Course class. 31 * 32 * @package tool_uploadcourse 33 * @copyright 2013 Frédéric Massart 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class tool_uploadcourse_course { 37 38 /** Outcome of the process: creating the course */ 39 const DO_CREATE = 1; 40 41 /** Outcome of the process: updating the course */ 42 const DO_UPDATE = 2; 43 44 /** Outcome of the process: deleting the course */ 45 const DO_DELETE = 3; 46 47 /** @var array final import data. */ 48 protected $data = array(); 49 50 /** @var array default values. */ 51 protected $defaults = array(); 52 53 /** @var array enrolment data. */ 54 protected $enrolmentdata; 55 56 /** @var array errors. */ 57 protected $errors = array(); 58 59 /** @var int the ID of the course that had been processed. */ 60 protected $id; 61 62 /** @var array containing options passed from the processor. */ 63 protected $importoptions = array(); 64 65 /** @var int import mode. Matches tool_uploadcourse_processor::MODE_* */ 66 protected $mode; 67 68 /** @var array course import options. */ 69 protected $options = array(); 70 71 /** @var int constant value of self::DO_*, what to do with that course */ 72 protected $do; 73 74 /** @var bool set to true once we have prepared the course */ 75 protected $prepared = false; 76 77 /** @var bool set to true once we have started the process of the course */ 78 protected $processstarted = false; 79 80 /** @var array course import data. */ 81 protected $rawdata = array(); 82 83 /** @var array restore directory. */ 84 protected $restoredata; 85 86 /** @var string course shortname. */ 87 protected $shortname; 88 89 /** @var array errors. */ 90 protected $statuses = array(); 91 92 /** @var int update mode. Matches tool_uploadcourse_processor::UPDATE_* */ 93 protected $updatemode; 94 95 /** @var array fields allowed as course data. */ 96 static protected $validfields = array('fullname', 'shortname', 'idnumber', 'category', 'visible', 'startdate', 97 'summary', 'format', 'theme', 'lang', 'newsitems', 'showgrades', 'showreports', 'legacyfiles', 'maxbytes', 98 'groupmode', 'groupmodeforce', 'groupmodeforce', 'enablecompletion'); 99 100 /** @var array fields required on course creation. */ 101 static protected $mandatoryfields = array('fullname', 'category'); 102 103 /** @var array fields which are considered as options. */ 104 static protected $optionfields = array('delete' => false, 'rename' => null, 'backupfile' => null, 105 'templatecourse' => null, 'reset' => false); 106 107 /** @var array options determining what can or cannot be done at an import level. */ 108 static protected $importoptionsdefaults = array('canrename' => false, 'candelete' => false, 'canreset' => false, 109 'reset' => false, 'restoredir' => null, 'shortnametemplate' => null); 110 111 /** 112 * Constructor 113 * 114 * @param int $mode import mode, constant matching tool_uploadcourse_processor::MODE_* 115 * @param int $updatemode update mode, constant matching tool_uploadcourse_processor::UPDATE_* 116 * @param array $rawdata raw course data. 117 * @param array $defaults default course data. 118 * @param array $importoptions import options. 119 */ 120 public function __construct($mode, $updatemode, $rawdata, $defaults = array(), $importoptions = array()) { 121 122 if ($mode !== tool_uploadcourse_processor::MODE_CREATE_NEW && 123 $mode !== tool_uploadcourse_processor::MODE_CREATE_ALL && 124 $mode !== tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE && 125 $mode !== tool_uploadcourse_processor::MODE_UPDATE_ONLY) { 126 throw new coding_exception('Incorrect mode.'); 127 } else if ($updatemode !== tool_uploadcourse_processor::UPDATE_NOTHING && 128 $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY && 129 $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS && 130 $updatemode !== tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS) { 131 throw new coding_exception('Incorrect update mode.'); 132 } 133 134 $this->mode = $mode; 135 $this->updatemode = $updatemode; 136 137 if (isset($rawdata['shortname'])) { 138 $this->shortname = $rawdata['shortname']; 139 } 140 $this->rawdata = $rawdata; 141 $this->defaults = $defaults; 142 143 // Extract course options. 144 foreach (self::$optionfields as $option => $default) { 145 $this->options[$option] = isset($rawdata[$option]) ? $rawdata[$option] : $default; 146 } 147 148 // Import options. 149 foreach (self::$importoptionsdefaults as $option => $default) { 150 $this->importoptions[$option] = isset($importoptions[$option]) ? $importoptions[$option] : $default; 151 } 152 } 153 154 /** 155 * Does the mode allow for course creation? 156 * 157 * @return bool 158 */ 159 public function can_create() { 160 return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, 161 tool_uploadcourse_processor::MODE_CREATE_NEW, 162 tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE) 163 ); 164 } 165 166 /** 167 * Does the mode allow for course deletion? 168 * 169 * @return bool 170 */ 171 public function can_delete() { 172 return $this->importoptions['candelete']; 173 } 174 175 /** 176 * Does the mode only allow for course creation? 177 * 178 * @return bool 179 */ 180 public function can_only_create() { 181 return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, 182 tool_uploadcourse_processor::MODE_CREATE_NEW)); 183 } 184 185 /** 186 * Does the mode allow for course rename? 187 * 188 * @return bool 189 */ 190 public function can_rename() { 191 return $this->importoptions['canrename']; 192 } 193 194 /** 195 * Does the mode allow for course reset? 196 * 197 * @return bool 198 */ 199 public function can_reset() { 200 return $this->importoptions['canreset']; 201 } 202 203 /** 204 * Does the mode allow for course update? 205 * 206 * @return bool 207 */ 208 public function can_update() { 209 return in_array($this->mode, 210 array( 211 tool_uploadcourse_processor::MODE_UPDATE_ONLY, 212 tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE) 213 ) && $this->updatemode != tool_uploadcourse_processor::UPDATE_NOTHING; 214 } 215 216 /** 217 * Can we use default values? 218 * 219 * @return bool 220 */ 221 public function can_use_defaults() { 222 return in_array($this->updatemode, array(tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS, 223 tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS)); 224 } 225 226 /** 227 * Delete the current course. 228 * 229 * @return bool 230 */ 231 protected function delete() { 232 global $DB; 233 $this->id = $DB->get_field_select('course', 'id', 'shortname = :shortname', 234 array('shortname' => $this->shortname), MUST_EXIST); 235 return delete_course($this->id, false); 236 } 237 238 /** 239 * Log an error 240 * 241 * @param string $code error code. 242 * @param lang_string $message error message. 243 * @return void 244 */ 245 protected function error($code, lang_string $message) { 246 if (array_key_exists($code, $this->errors)) { 247 throw new coding_exception('Error code already defined'); 248 } 249 $this->errors[$code] = $message; 250 } 251 252 /** 253 * Return whether the course exists or not. 254 * 255 * @param string $shortname the shortname to use to check if the course exists. Falls back on $this->shortname if empty. 256 * @return bool 257 */ 258 protected function exists($shortname = null) { 259 global $DB; 260 if (is_null($shortname)) { 261 $shortname = $this->shortname; 262 } 263 if (!empty($shortname) || is_numeric($shortname)) { 264 return $DB->record_exists('course', array('shortname' => $shortname)); 265 } 266 return false; 267 } 268 269 /** 270 * Return the data that will be used upon saving. 271 * 272 * @return null|array 273 */ 274 public function get_data() { 275 return $this->data; 276 } 277 278 /** 279 * Return the errors found during preparation. 280 * 281 * @return array 282 */ 283 public function get_errors() { 284 return $this->errors; 285 } 286 287 /** 288 * Assemble the course data based on defaults. 289 * 290 * This returns the final data to be passed to create_course(). 291 * 292 * @param array $data current data. 293 * @return array 294 */ 295 protected function get_final_create_data($data) { 296 foreach (self::$validfields as $field) { 297 if (!isset($data[$field]) && isset($this->defaults[$field])) { 298 $data[$field] = $this->defaults[$field]; 299 } 300 } 301 $data['shortname'] = $this->shortname; 302 return $data; 303 } 304 305 /** 306 * Assemble the course data based on defaults. 307 * 308 * This returns the final data to be passed to update_course(). 309 * 310 * @param array $data current data. 311 * @param bool $usedefaults are defaults allowed? 312 * @param bool $missingonly ignore fields which are already set. 313 * @return array 314 */ 315 protected function get_final_update_data($data, $usedefaults = false, $missingonly = false) { 316 global $DB; 317 $newdata = array(); 318 $existingdata = $DB->get_record('course', array('shortname' => $this->shortname)); 319 foreach (self::$validfields as $field) { 320 if ($missingonly) { 321 if (!is_null($existingdata->$field) and $existingdata->$field !== '') { 322 continue; 323 } 324 } 325 if (isset($data[$field])) { 326 $newdata[$field] = $data[$field]; 327 } else if ($usedefaults && isset($this->defaults[$field])) { 328 $newdata[$field] = $this->defaults[$field]; 329 } 330 } 331 $newdata['id'] = $existingdata->id; 332 return $newdata; 333 } 334 335 /** 336 * Return the ID of the processed course. 337 * 338 * @return int|null 339 */ 340 public function get_id() { 341 if (!$this->processstarted) { 342 throw new coding_exception('The course has not been processed yet!'); 343 } 344 return $this->id; 345 } 346 347 /** 348 * Get the directory of the object to restore. 349 * 350 * @return string|false|null subdirectory in $CFG->tempdir/backup/..., false when an error occured 351 * and null when there is simply nothing. 352 */ 353 protected function get_restore_content_dir() { 354 $backupfile = null; 355 $shortname = null; 356 357 if (!empty($this->options['backupfile'])) { 358 $backupfile = $this->options['backupfile']; 359 } else if (!empty($this->options['templatecourse']) || is_numeric($this->options['templatecourse'])) { 360 $shortname = $this->options['templatecourse']; 361 } 362 363 $errors = array(); 364 $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname, $errors); 365 if (!empty($errors)) { 366 foreach ($errors as $key => $message) { 367 $this->error($key, $message); 368 } 369 return false; 370 } else if ($dir === false) { 371 // We want to return null when nothing was wrong, but nothing was found. 372 $dir = null; 373 } 374 375 if (empty($dir) && !empty($this->importoptions['restoredir'])) { 376 $dir = $this->importoptions['restoredir']; 377 } 378 379 return $dir; 380 } 381 382 /** 383 * Return the errors found during preparation. 384 * 385 * @return array 386 */ 387 public function get_statuses() { 388 return $this->statuses; 389 } 390 391 /** 392 * Return whether there were errors with this course. 393 * 394 * @return boolean 395 */ 396 public function has_errors() { 397 return !empty($this->errors); 398 } 399 400 /** 401 * Validates and prepares the data. 402 * 403 * @return bool false is any error occured. 404 */ 405 public function prepare() { 406 global $DB, $SITE; 407 $this->prepared = true; 408 409 // Validate the shortname. 410 if (!empty($this->shortname) || is_numeric($this->shortname)) { 411 if ($this->shortname !== clean_param($this->shortname, PARAM_TEXT)) { 412 $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); 413 return false; 414 } 415 } 416 417 $exists = $this->exists(); 418 419 // Do we want to delete the course? 420 if ($this->options['delete']) { 421 if (!$exists) { 422 $this->error('cannotdeletecoursenotexist', new lang_string('cannotdeletecoursenotexist', 'tool_uploadcourse')); 423 return false; 424 } else if (!$this->can_delete()) { 425 $this->error('coursedeletionnotallowed', new lang_string('coursedeletionnotallowed', 'tool_uploadcourse')); 426 return false; 427 } 428 429 $this->do = self::DO_DELETE; 430 return true; 431 } 432 433 // Can we create/update the course under those conditions? 434 if ($exists) { 435 if ($this->mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { 436 $this->error('courseexistsanduploadnotallowed', 437 new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); 438 return false; 439 } else if ($this->can_update()) { 440 // We can never allow for any front page changes! 441 if ($this->shortname == $SITE->shortname) { 442 $this->error('cannotupdatefrontpage', new lang_string('cannotupdatefrontpage', 'tool_uploadcourse')); 443 return false; 444 } 445 } 446 } else { 447 if (!$this->can_create()) { 448 $this->error('coursedoesnotexistandcreatenotallowed', 449 new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); 450 return false; 451 } 452 } 453 454 // Basic data. 455 $coursedata = array(); 456 foreach ($this->rawdata as $field => $value) { 457 if (!in_array($field, self::$validfields)) { 458 continue; 459 } else if ($field == 'shortname') { 460 // Let's leave it apart from now, use $this->shortname only. 461 continue; 462 } 463 $coursedata[$field] = $value; 464 } 465 466 $mode = $this->mode; 467 $updatemode = $this->updatemode; 468 $usedefaults = $this->can_use_defaults(); 469 470 // Resolve the category, and fail if not found. 471 $errors = array(); 472 $catid = tool_uploadcourse_helper::resolve_category($this->rawdata, $errors); 473 if (empty($errors)) { 474 $coursedata['category'] = $catid; 475 } else { 476 foreach ($errors as $key => $message) { 477 $this->error($key, $message); 478 } 479 return false; 480 } 481 482 // If the course does not exist, or will be forced created. 483 if (!$exists || $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { 484 485 // Mandatory fields upon creation. 486 $errors = array(); 487 foreach (self::$mandatoryfields as $field) { 488 if ((!isset($coursedata[$field]) || $coursedata[$field] === '') && 489 (!isset($this->defaults[$field]) || $this->defaults[$field] === '')) { 490 $errors[] = $field; 491 } 492 } 493 if (!empty($errors)) { 494 $this->error('missingmandatoryfields', new lang_string('missingmandatoryfields', 'tool_uploadcourse', 495 implode(', ', $errors))); 496 return false; 497 } 498 } 499 500 // Should the course be renamed? 501 if (!empty($this->options['rename']) || is_numeric($this->options['rename'])) { 502 if (!$this->can_update()) { 503 $this->error('canonlyrenameinupdatemode', new lang_string('canonlyrenameinupdatemode', 'tool_uploadcourse')); 504 return false; 505 } else if (!$exists) { 506 $this->error('cannotrenamecoursenotexist', new lang_string('cannotrenamecoursenotexist', 'tool_uploadcourse')); 507 return false; 508 } else if (!$this->can_rename()) { 509 $this->error('courserenamingnotallowed', new lang_string('courserenamingnotallowed', 'tool_uploadcourse')); 510 return false; 511 } else if ($this->options['rename'] !== clean_param($this->options['rename'], PARAM_TEXT)) { 512 $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); 513 return false; 514 } else if ($this->exists($this->options['rename'])) { 515 $this->error('cannotrenameshortnamealreadyinuse', 516 new lang_string('cannotrenameshortnamealreadyinuse', 'tool_uploadcourse')); 517 return false; 518 } else if (isset($coursedata['idnumber']) && 519 $DB->count_records_select('course', 'idnumber = :idn AND shortname != :sn', 520 array('idn' => $coursedata['idnumber'], 'sn' => $this->shortname)) > 0) { 521 $this->error('cannotrenameidnumberconflict', new lang_string('cannotrenameidnumberconflict', 'tool_uploadcourse')); 522 return false; 523 } 524 $coursedata['shortname'] = $this->options['rename']; 525 $this->status('courserenamed', new lang_string('courserenamed', 'tool_uploadcourse', 526 array('from' => $this->shortname, 'to' => $coursedata['shortname']))); 527 } 528 529 // Should we generate a shortname? 530 if (empty($this->shortname) && !is_numeric($this->shortname)) { 531 if (empty($this->importoptions['shortnametemplate'])) { 532 $this->error('missingshortnamenotemplate', new lang_string('missingshortnamenotemplate', 'tool_uploadcourse')); 533 return false; 534 } else if (!$this->can_only_create()) { 535 $this->error('cannotgenerateshortnameupdatemode', 536 new lang_string('cannotgenerateshortnameupdatemode', 'tool_uploadcourse')); 537 return false; 538 } else { 539 $newshortname = tool_uploadcourse_helper::generate_shortname($coursedata, 540 $this->importoptions['shortnametemplate']); 541 if (is_null($newshortname)) { 542 $this->error('generatedshortnameinvalid', new lang_string('generatedshortnameinvalid', 'tool_uploadcourse')); 543 return false; 544 } else if ($this->exists($newshortname)) { 545 if ($mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { 546 $this->error('generatedshortnamealreadyinuse', 547 new lang_string('generatedshortnamealreadyinuse', 'tool_uploadcourse')); 548 return false; 549 } 550 $exists = true; 551 } 552 $this->status('courseshortnamegenerated', new lang_string('courseshortnamegenerated', 'tool_uploadcourse', 553 $newshortname)); 554 $this->shortname = $newshortname; 555 } 556 } 557 558 // If exists, but we only want to create courses, increment the shortname. 559 if ($exists && $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { 560 $original = $this->shortname; 561 $this->shortname = tool_uploadcourse_helper::increment_shortname($this->shortname); 562 $exists = false; 563 if ($this->shortname != $original) { 564 $this->status('courseshortnameincremented', new lang_string('courseshortnameincremented', 'tool_uploadcourse', 565 array('from' => $original, 'to' => $this->shortname))); 566 if (isset($coursedata['idnumber'])) { 567 $originalidn = $coursedata['idnumber']; 568 $coursedata['idnumber'] = tool_uploadcourse_helper::increment_idnumber($coursedata['idnumber']); 569 if ($originalidn != $coursedata['idnumber']) { 570 $this->status('courseidnumberincremented', new lang_string('courseidnumberincremented', 'tool_uploadcourse', 571 array('from' => $originalidn, 'to' => $coursedata['idnumber']))); 572 } 573 } 574 } 575 } 576 577 // If the course does not exist, ensure that the ID number is not taken. 578 if (!$exists && isset($coursedata['idnumber'])) { 579 if ($DB->count_records_select('course', 'idnumber = :idn', array('idn' => $coursedata['idnumber'])) > 0) { 580 $this->error('idnumberalreadyinuse', new lang_string('idnumberalreadyinuse', 'tool_uploadcourse')); 581 return false; 582 } 583 } 584 585 // Ultimate check mode vs. existence. 586 switch ($mode) { 587 case tool_uploadcourse_processor::MODE_CREATE_NEW: 588 case tool_uploadcourse_processor::MODE_CREATE_ALL: 589 if ($exists) { 590 $this->error('courseexistsanduploadnotallowed', 591 new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); 592 return false; 593 } 594 break; 595 case tool_uploadcourse_processor::MODE_UPDATE_ONLY: 596 if (!$exists) { 597 $this->error('coursedoesnotexistandcreatenotallowed', 598 new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); 599 return false; 600 } 601 // No break! 602 case tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE: 603 if ($exists) { 604 if ($updatemode === tool_uploadcourse_processor::UPDATE_NOTHING) { 605 $this->error('updatemodedoessettonothing', 606 new lang_string('updatemodedoessettonothing', 'tool_uploadcourse')); 607 return false; 608 } 609 } 610 break; 611 default: 612 // O_o Huh?! This should really never happen here! 613 $this->error('unknownimportmode', new lang_string('unknownimportmode', 'tool_uploadcourse')); 614 return false; 615 } 616 617 // Get final data. 618 if ($exists) { 619 $missingonly = ($updatemode === tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS); 620 $coursedata = $this->get_final_update_data($coursedata, $usedefaults, $missingonly); 621 622 // Make sure we are not trying to mess with the front page, though we should never get here! 623 if ($coursedata['id'] == $SITE->id) { 624 $this->error('cannotupdatefrontpage', new lang_string('cannotupdatefrontpage', 'tool_uploadcourse')); 625 return false; 626 } 627 628 $this->do = self::DO_UPDATE; 629 } else { 630 $coursedata = $this->get_final_create_data($coursedata); 631 $this->do = self::DO_CREATE; 632 } 633 634 // Course start date. 635 if (!empty($coursedata['startdate'])) { 636 $coursedata['startdate'] = strtotime($coursedata['startdate']); 637 } 638 639 // Add role renaming. 640 $errors = array(); 641 $rolenames = tool_uploadcourse_helper::get_role_names($this->rawdata, $errors); 642 if (!empty($errors)) { 643 foreach ($errors as $key => $message) { 644 $this->error($key, $message); 645 } 646 return false; 647 } 648 foreach ($rolenames as $rolekey => $rolename) { 649 $coursedata[$rolekey] = $rolename; 650 } 651 652 // Some validation. 653 if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) { 654 $this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse')); 655 return false; 656 } 657 658 // Saving data. 659 $this->data = $coursedata; 660 $this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata); 661 662 if (isset($this->rawdata['tags']) && strval($this->rawdata['tags']) !== '') { 663 $this->data['tags'] = preg_split('/\s*,\s*/', trim($this->rawdata['tags']), -1, PREG_SPLIT_NO_EMPTY); 664 } 665 666 // Restore data. 667 // TODO Speed up things by not really extracting the backup just yet, but checking that 668 // the backup file or shortname passed are valid. Extraction should happen in proceed(). 669 $this->restoredata = $this->get_restore_content_dir(); 670 if ($this->restoredata === false) { 671 return false; 672 } 673 674 // We can only reset courses when allowed and we are updating the course. 675 if ($this->importoptions['reset'] || $this->options['reset']) { 676 if ($this->do !== self::DO_UPDATE) { 677 $this->error('canonlyresetcourseinupdatemode', 678 new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse')); 679 return false; 680 } else if (!$this->can_reset()) { 681 $this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse')); 682 return false; 683 } 684 } 685 686 return true; 687 } 688 689 /** 690 * Proceed with the import of the course. 691 * 692 * @return void 693 */ 694 public function proceed() { 695 global $CFG, $USER; 696 697 if (!$this->prepared) { 698 throw new coding_exception('The course has not been prepared.'); 699 } else if ($this->has_errors()) { 700 throw new moodle_exception('Cannot proceed, errors were detected.'); 701 } else if ($this->processstarted) { 702 throw new coding_exception('The process has already been started.'); 703 } 704 $this->processstarted = true; 705 706 if ($this->do === self::DO_DELETE) { 707 if ($this->delete()) { 708 $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse')); 709 } else { 710 $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse')); 711 } 712 return true; 713 } else if ($this->do === self::DO_CREATE) { 714 $course = create_course((object) $this->data); 715 $this->id = $course->id; 716 $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse')); 717 } else if ($this->do === self::DO_UPDATE) { 718 $course = (object) $this->data; 719 update_course($course); 720 $this->id = $course->id; 721 $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse')); 722 } else { 723 // Strangely the outcome has not been defined, or is unknown! 724 throw new coding_exception('Unknown outcome!'); 725 } 726 727 // Restore a course. 728 if (!empty($this->restoredata)) { 729 $rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO, 730 backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); 731 732 // Check if the format conversion must happen first. 733 if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { 734 $rc->convert(); 735 } 736 if ($rc->execute_precheck()) { 737 $rc->execute_plan(); 738 $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse')); 739 } else { 740 $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse')); 741 } 742 $rc->destroy(); 743 } 744 745 // Proceed with enrolment data. 746 $this->process_enrolment_data($course); 747 748 // Reset the course. 749 if ($this->importoptions['reset'] || $this->options['reset']) { 750 if ($this->do === self::DO_UPDATE && $this->can_reset()) { 751 $this->reset($course); 752 $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse')); 753 } 754 } 755 756 // Mark context as dirty. 757 $context = context_course::instance($course->id); 758 $context->mark_dirty(); 759 } 760 761 /** 762 * Add the enrolment data for the course. 763 * 764 * @param object $course course record. 765 * @return void 766 */ 767 protected function process_enrolment_data($course) { 768 global $DB; 769 770 $enrolmentdata = $this->enrolmentdata; 771 if (empty($enrolmentdata)) { 772 return; 773 } 774 775 $enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins(); 776 $instances = enrol_get_instances($course->id, false); 777 foreach ($enrolmentdata as $enrolmethod => $method) { 778 779 $instance = null; 780 foreach ($instances as $i) { 781 if ($i->enrol == $enrolmethod) { 782 $instance = $i; 783 break; 784 } 785 } 786 787 $todelete = isset($method['delete']) && $method['delete']; 788 $todisable = isset($method['disable']) && $method['disable']; 789 unset($method['delete']); 790 unset($method['disable']); 791 792 if (!empty($instance) && $todelete) { 793 // Remove the enrolment method. 794 foreach ($instances as $instance) { 795 if ($instance->enrol == $enrolmethod) { 796 $plugin = $enrolmentplugins[$instance->enrol]; 797 $plugin->delete_instance($instance); 798 break; 799 } 800 } 801 } else if (!empty($instance) && $todisable) { 802 // Disable the enrolment. 803 foreach ($instances as $instance) { 804 if ($instance->enrol == $enrolmethod) { 805 $plugin = $enrolmentplugins[$instance->enrol]; 806 $plugin->update_status($instance, ENROL_INSTANCE_DISABLED); 807 $enrol_updated = true; 808 break; 809 } 810 } 811 } else { 812 $plugin = null; 813 if (empty($instance)) { 814 $plugin = $enrolmentplugins[$enrolmethod]; 815 $instance = new stdClass(); 816 $instance->id = $plugin->add_default_instance($course); 817 $instance->roleid = $plugin->get_config('roleid'); 818 $instance->status = ENROL_INSTANCE_ENABLED; 819 } else { 820 $plugin = $enrolmentplugins[$instance->enrol]; 821 $plugin->update_status($instance, ENROL_INSTANCE_ENABLED); 822 } 823 824 // Now update values. 825 foreach ($method as $k => $v) { 826 $instance->{$k} = $v; 827 } 828 829 // Sort out the start, end and date. 830 $instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0); 831 $instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0); 832 833 // Is the enrolment period set? 834 if (isset($method['enrolperiod']) && ! empty($method['enrolperiod'])) { 835 if (preg_match('/^\d+$/', $method['enrolperiod'])) { 836 $method['enrolperiod'] = (int) $method['enrolperiod']; 837 } else { 838 // Try and convert period to seconds. 839 $method['enrolperiod'] = strtotime('1970-01-01 GMT + ' . $method['enrolperiod']); 840 } 841 $instance->enrolperiod = $method['enrolperiod']; 842 } 843 if ($instance->enrolstartdate > 0 && isset($method['enrolperiod'])) { 844 $instance->enrolenddate = $instance->enrolstartdate + $method['enrolperiod']; 845 } 846 if ($instance->enrolenddate > 0) { 847 $instance->enrolperiod = $instance->enrolenddate - $instance->enrolstartdate; 848 } 849 if ($instance->enrolenddate < $instance->enrolstartdate) { 850 $instance->enrolenddate = $instance->enrolstartdate; 851 } 852 853 // Sort out the given role. This does not filter the roles allowed in the course. 854 if (isset($method['role'])) { 855 $roleids = tool_uploadcourse_helper::get_role_ids(); 856 if (isset($roleids[$method['role']])) { 857 $instance->roleid = $roleids[$method['role']]; 858 } 859 } 860 861 $instance->timemodified = time(); 862 $DB->update_record('enrol', $instance); 863 } 864 } 865 } 866 867 /** 868 * Reset the current course. 869 * 870 * This does not reset any of the content of the activities. 871 * 872 * @param stdClass $course the course object of the course to reset. 873 * @return array status array of array component, item, error. 874 */ 875 protected function reset($course) { 876 global $DB; 877 878 $resetdata = new stdClass(); 879 $resetdata->id = $course->id; 880 $resetdata->reset_start_date = time(); 881 $resetdata->reset_events = true; 882 $resetdata->reset_notes = true; 883 $resetdata->delete_blog_associations = true; 884 $resetdata->reset_completion = true; 885 $resetdata->reset_roles_overrides = true; 886 $resetdata->reset_roles_local = true; 887 $resetdata->reset_groups_members = true; 888 $resetdata->reset_groups_remove = true; 889 $resetdata->reset_groupings_members = true; 890 $resetdata->reset_groupings_remove = true; 891 $resetdata->reset_gradebook_items = true; 892 $resetdata->reset_gradebook_grades = true; 893 $resetdata->reset_comments = true; 894 895 if (empty($course->startdate)) { 896 $course->startdate = $DB->get_field_select('course', 'startdate', 'id = :id', array('id' => $course->id)); 897 } 898 $resetdata->reset_start_date_old = $course->startdate; 899 900 // Add roles. 901 $roles = tool_uploadcourse_helper::get_role_ids(); 902 $resetdata->unenrol_users = array_values($roles); 903 $resetdata->unenrol_users[] = 0; // Enrolled without role. 904 905 return reset_course_userdata($resetdata); 906 } 907 908 /** 909 * Log a status 910 * 911 * @param string $code status code. 912 * @param lang_string $message status message. 913 * @return void 914 */ 915 protected function status($code, lang_string $message) { 916 if (array_key_exists($code, $this->statuses)) { 917 throw new coding_exception('Status code already defined'); 918 } 919 $this->statuses[$code] = $message; 920 } 921 922 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |