[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/admin/tool/uploadcourse/classes/ -> course.php (source)

   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          // Restore data.
 663          // TODO Speed up things by not really extracting the backup just yet, but checking that
 664          // the backup file or shortname passed are valid. Extraction should happen in proceed().
 665          $this->restoredata = $this->get_restore_content_dir();
 666          if ($this->restoredata === false) {
 667              return false;
 668          }
 669  
 670          // We can only reset courses when allowed and we are updating the course.
 671          if ($this->importoptions['reset'] || $this->options['reset']) {
 672              if ($this->do !== self::DO_UPDATE) {
 673                  $this->error('canonlyresetcourseinupdatemode',
 674                      new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse'));
 675                  return false;
 676              } else if (!$this->can_reset()) {
 677                  $this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse'));
 678                  return false;
 679              }
 680          }
 681  
 682          return true;
 683      }
 684  
 685      /**
 686       * Proceed with the import of the course.
 687       *
 688       * @return void
 689       */
 690      public function proceed() {
 691          global $CFG, $USER;
 692  
 693          if (!$this->prepared) {
 694              throw new coding_exception('The course has not been prepared.');
 695          } else if ($this->has_errors()) {
 696              throw new moodle_exception('Cannot proceed, errors were detected.');
 697          } else if ($this->processstarted) {
 698              throw new coding_exception('The process has already been started.');
 699          }
 700          $this->processstarted = true;
 701  
 702          if ($this->do === self::DO_DELETE) {
 703              if ($this->delete()) {
 704                  $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse'));
 705              } else {
 706                  $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse'));
 707              }
 708              return true;
 709          } else if ($this->do === self::DO_CREATE) {
 710              $course = create_course((object) $this->data);
 711              $this->id = $course->id;
 712              $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse'));
 713          } else if ($this->do === self::DO_UPDATE) {
 714              $course = (object) $this->data;
 715              update_course($course);
 716              $this->id = $course->id;
 717              $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse'));
 718          } else {
 719              // Strangely the outcome has not been defined, or is unknown!
 720              throw new coding_exception('Unknown outcome!');
 721          }
 722  
 723          // Restore a course.
 724          if (!empty($this->restoredata)) {
 725              $rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO,
 726                  backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
 727  
 728              // Check if the format conversion must happen first.
 729              if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
 730                  $rc->convert();
 731              }
 732              if ($rc->execute_precheck()) {
 733                  $rc->execute_plan();
 734                  $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse'));
 735              } else {
 736                  $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse'));
 737              }
 738              $rc->destroy();
 739              unset($rc); // File logging is a mess, we can only try to rely on gc to close handles.
 740          }
 741  
 742          // Proceed with enrolment data.
 743          $this->process_enrolment_data($course);
 744  
 745          // Reset the course.
 746          if ($this->importoptions['reset'] || $this->options['reset']) {
 747              if ($this->do === self::DO_UPDATE && $this->can_reset()) {
 748                  $this->reset($course);
 749                  $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse'));
 750              }
 751          }
 752  
 753          // Mark context as dirty.
 754          $context = context_course::instance($course->id);
 755          $context->mark_dirty();
 756      }
 757  
 758      /**
 759       * Add the enrolment data for the course.
 760       *
 761       * @param object $course course record.
 762       * @return void
 763       */
 764      protected function process_enrolment_data($course) {
 765          global $DB;
 766  
 767          $enrolmentdata = $this->enrolmentdata;
 768          if (empty($enrolmentdata)) {
 769              return;
 770          }
 771  
 772          $enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins();
 773          $instances = enrol_get_instances($course->id, false);
 774          foreach ($enrolmentdata as $enrolmethod => $method) {
 775  
 776              $instance = null;
 777              foreach ($instances as $i) {
 778                  if ($i->enrol == $enrolmethod) {
 779                      $instance = $i;
 780                      break;
 781                  }
 782              }
 783  
 784              $todelete = isset($method['delete']) && $method['delete'];
 785              $todisable = isset($method['disable']) && $method['disable'];
 786              unset($method['delete']);
 787              unset($method['disable']);
 788  
 789              if (!empty($instance) && $todelete) {
 790                  // Remove the enrolment method.
 791                  foreach ($instances as $instance) {
 792                      if ($instance->enrol == $enrolmethod) {
 793                          $plugin = $enrolmentplugins[$instance->enrol];
 794                          $plugin->delete_instance($instance);
 795                          break;
 796                      }
 797                  }
 798              } else if (!empty($instance) && $todisable) {
 799                  // Disable the enrolment.
 800                  foreach ($instances as $instance) {
 801                      if ($instance->enrol == $enrolmethod) {
 802                          $plugin = $enrolmentplugins[$instance->enrol];
 803                          $plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
 804                          $enrol_updated = true;
 805                          break;
 806                      }
 807                  }
 808              } else {
 809                  $plugin = null;
 810                  if (empty($instance)) {
 811                      $plugin = $enrolmentplugins[$enrolmethod];
 812                      $instance = new stdClass();
 813                      $instance->id = $plugin->add_default_instance($course);
 814                      $instance->roleid = $plugin->get_config('roleid');
 815                      $instance->status = ENROL_INSTANCE_ENABLED;
 816                  } else {
 817                      $plugin = $enrolmentplugins[$instance->enrol];
 818                      $plugin->update_status($instance, ENROL_INSTANCE_ENABLED);
 819                  }
 820  
 821                  // Now update values.
 822                  foreach ($method as $k => $v) {
 823                      $instance->{$k} = $v;
 824                  }
 825  
 826                  // Sort out the start, end and date.
 827                  $instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0);
 828                  $instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0);
 829  
 830                  // Is the enrolment period set?
 831                  if (isset($method['enrolperiod']) && ! empty($method['enrolperiod'])) {
 832                      if (preg_match('/^\d+$/', $method['enrolperiod'])) {
 833                          $method['enrolperiod'] = (int) $method['enrolperiod'];
 834                      } else {
 835                          // Try and convert period to seconds.
 836                          $method['enrolperiod'] = strtotime('1970-01-01 GMT + ' . $method['enrolperiod']);
 837                      }
 838                      $instance->enrolperiod = $method['enrolperiod'];
 839                  }
 840                  if ($instance->enrolstartdate > 0 && isset($method['enrolperiod'])) {
 841                      $instance->enrolenddate = $instance->enrolstartdate + $method['enrolperiod'];
 842                  }
 843                  if ($instance->enrolenddate > 0) {
 844                      $instance->enrolperiod = $instance->enrolenddate - $instance->enrolstartdate;
 845                  }
 846                  if ($instance->enrolenddate < $instance->enrolstartdate) {
 847                      $instance->enrolenddate = $instance->enrolstartdate;
 848                  }
 849  
 850                  // Sort out the given role. This does not filter the roles allowed in the course.
 851                  if (isset($method['role'])) {
 852                      $roleids = tool_uploadcourse_helper::get_role_ids();
 853                      if (isset($roleids[$method['role']])) {
 854                          $instance->roleid = $roleids[$method['role']];
 855                      }
 856                  }
 857  
 858                  $instance->timemodified = time();
 859                  $DB->update_record('enrol', $instance);
 860              }
 861          }
 862      }
 863  
 864      /**
 865       * Reset the current course.
 866       *
 867       * This does not reset any of the content of the activities.
 868       *
 869       * @param stdClass $course the course object of the course to reset.
 870       * @return array status array of array component, item, error.
 871       */
 872      protected function reset($course) {
 873          global $DB;
 874  
 875          $resetdata = new stdClass();
 876          $resetdata->id = $course->id;
 877          $resetdata->reset_start_date = time();
 878          $resetdata->reset_events = true;
 879          $resetdata->reset_notes = true;
 880          $resetdata->delete_blog_associations = true;
 881          $resetdata->reset_completion = true;
 882          $resetdata->reset_roles_overrides = true;
 883          $resetdata->reset_roles_local = true;
 884          $resetdata->reset_groups_members = true;
 885          $resetdata->reset_groups_remove = true;
 886          $resetdata->reset_groupings_members = true;
 887          $resetdata->reset_groupings_remove = true;
 888          $resetdata->reset_gradebook_items = true;
 889          $resetdata->reset_gradebook_grades = true;
 890          $resetdata->reset_comments = true;
 891  
 892          if (empty($course->startdate)) {
 893              $course->startdate = $DB->get_field_select('course', 'startdate', 'id = :id', array('id' => $course->id));
 894          }
 895          $resetdata->reset_start_date_old = $course->startdate;
 896  
 897          // Add roles.
 898          $roles = tool_uploadcourse_helper::get_role_ids();
 899          $resetdata->unenrol_users = array_values($roles);
 900          $resetdata->unenrol_users[] = 0;    // Enrolled without role.
 901  
 902          return reset_course_userdata($resetdata);
 903      }
 904  
 905      /**
 906       * Log a status
 907       *
 908       * @param string $code status code.
 909       * @param lang_string $message status message.
 910       * @return void
 911       */
 912      protected function status($code, lang_string $message) {
 913          if (array_key_exists($code, $this->statuses)) {
 914              throw new coding_exception('Status code already defined');
 915          }
 916          $this->statuses[$code] = $message;
 917      }
 918  
 919  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1