[ Index ]

PHP Cross Reference of Unnamed Project

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          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  }


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