[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/scorm/datamodels/ -> aicclib.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   * functions used by AICC packages.
  19   *
  20   * @package    mod_scorm
  21   * @copyright 1999 onwards Roberto Pinna
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  function scorm_add_time($a, $b) {
  26      $aes = explode(':', $a);
  27      $bes = explode(':', $b);
  28      $aseconds = explode('.', $aes[2]);
  29      $bseconds = explode('.', $bes[2]);
  30      $change = 0;
  31  
  32      $acents = 0;  // Cents.
  33      if (count($aseconds) > 1) {
  34          $acents = $aseconds[1];
  35      }
  36      $bcents = 0;
  37      if (count($bseconds) > 1) {
  38          $bcents = $bseconds[1];
  39      }
  40      $cents = $acents + $bcents;
  41      $change = floor($cents / 100);
  42      $cents = $cents - ($change * 100);
  43      if (floor($cents) < 10) {
  44          $cents = '0'. $cents;
  45      }
  46  
  47      $secs = $aseconds[0] + $bseconds[0] + $change;  // Seconds.
  48      $change = floor($secs / 60);
  49      $secs = $secs - ($change * 60);
  50      if (floor($secs) < 10) {
  51          $secs = '0'. $secs;
  52      }
  53  
  54      $mins = $aes[1] + $bes[1] + $change;   // Minutes.
  55      $change = floor($mins / 60);
  56      $mins = $mins - ($change * 60);
  57      if ($mins < 10) {
  58          $mins = '0' .  $mins;
  59      }
  60  
  61      $hours = $aes[0] + $bes[0] + $change;  // Hours.
  62      if ($hours < 10) {
  63          $hours = '0' . $hours;
  64      }
  65  
  66      if ($cents != '0') {
  67          return $hours . ":" . $mins . ":" . $secs . '.' . $cents;
  68      } else {
  69          return $hours . ":" . $mins . ":" . $secs;
  70      }
  71  }
  72  
  73  /**
  74   * Take the header row of an AICC definition file
  75   * and returns sequence of columns and a pointer to
  76   * the sco identifier column.
  77   *
  78   * @param string $row AICC header row
  79   * @param string $mastername AICC sco identifier column
  80   * @return mixed
  81   */
  82  function scorm_get_aicc_columns($row, $mastername='system_id') {
  83      $tok = strtok(strtolower($row), "\",\n\r");
  84      $result = new stdClass();
  85      $result->columns = array();
  86      $i = 0;
  87      while ($tok) {
  88          if ($tok != '') {
  89              $result->columns[] = $tok;
  90              if ($tok == $mastername) {
  91                  $result->mastercol = $i;
  92              }
  93              $i++;
  94          }
  95          $tok = strtok("\",\n\r");
  96      }
  97      return $result;
  98  }
  99  
 100  /**
 101   * Given a colums array return a string containing the regular
 102   * expression to match the columns in a text row.
 103   *
 104   * @param array $column The header columns
 105   * @param string $remodule The regular expression module for a single column
 106   * @return string
 107   */
 108  function scorm_forge_cols_regexp($columns, $remodule='(".*")?,') {
 109      $regexp = '/^';
 110      foreach ($columns as $column) {
 111          $regexp .= $remodule;
 112      }
 113      $regexp = substr($regexp, 0, -1) . '/';
 114      return $regexp;
 115  }
 116  
 117  /**
 118   * Sets up AICC packages
 119   * Called whenever package changes
 120   * @param object $scorm instance - fields are updated and changes saved into database
 121   * @return bool
 122   */
 123  function scorm_parse_aicc(&$scorm) {
 124      global $DB;
 125  
 126      if ($scorm->scormtype == SCORM_TYPE_AICCURL) {
 127          return scorm_aicc_generate_simple_sco($scorm);
 128      }
 129      if (!isset($scorm->cmid)) {
 130          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 131          $scorm->cmid = $cm->id;
 132      }
 133      $context = context_module::instance($scorm->cmid);
 134  
 135      $fs = get_file_storage();
 136  
 137      $files = $fs->get_area_files($context->id, 'mod_scorm', 'content', 0, 'sortorder, itemid, filepath, filename', false);
 138  
 139      $version = 'AICC';
 140      $ids = array();
 141      $courses = array();
 142      $extaiccfiles = array('crs', 'des', 'au', 'cst', 'ort', 'pre', 'cmp');
 143  
 144      foreach ($files as $file) {
 145          $filename = $file->get_filename();
 146          $ext = substr($filename, strrpos($filename, '.'));
 147          $extension = strtolower(substr($ext, 1));
 148          if (in_array($extension, $extaiccfiles)) {
 149              $id = strtolower(basename($filename, $ext));
 150              if (!isset($ids[$id])) {
 151                  $ids[$id] = new stdClass();
 152              }
 153              $ids[$id]->$extension = $file;
 154          }
 155      }
 156  
 157      foreach ($ids as $courseid => $id) {
 158          if (!isset($courses[$courseid])) {
 159              $courses[$courseid] = new stdClass();
 160          }
 161          if (isset($id->crs)) {
 162              $contents = $id->crs->get_content();
 163              $rows = explode("\r\n", $contents);
 164              if (is_array($rows)) {
 165                  foreach ($rows as $row) {
 166                      if (preg_match("/^(.+)=(.+)$/", $row, $matches)) {
 167                          switch (strtolower(trim($matches[1]))) {
 168                              case 'course_id':
 169                                  $courses[$courseid]->id = trim($matches[2]);
 170                              break;
 171                              case 'course_title':
 172                                  $courses[$courseid]->title = trim($matches[2]);
 173                              break;
 174                              case 'version':
 175                                  $courses[$courseid]->version = 'AICC_'.trim($matches[2]);
 176                              break;
 177                          }
 178                      }
 179                  }
 180              }
 181          }
 182          if (isset($id->des)) {
 183              $contents = $id->des->get_content();
 184              $rows = explode("\r\n", $contents);
 185              $columns = scorm_get_aicc_columns($rows[0]);
 186              $regexp = scorm_forge_cols_regexp($columns->columns);
 187              for ($i = 1; $i < count($rows); $i++) {
 188                  if (preg_match($regexp, $rows[$i], $matches)) {
 189                      for ($j = 0; $j < count($columns->columns); $j++) {
 190                          $column = $columns->columns[$j];
 191                          if (!isset($courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)])) {
 192                              $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)] = new stdClass();
 193                          }
 194                          $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
 195                      }
 196                  }
 197              }
 198          }
 199          if (isset($id->au)) {
 200              $contents = $id->au->get_content();
 201              $rows = explode("\r\n", $contents);
 202              $columns = scorm_get_aicc_columns($rows[0]);
 203              $regexp = scorm_forge_cols_regexp($columns->columns);
 204              for ($i = 1; $i < count($rows); $i++) {
 205                  if (preg_match($regexp, $rows[$i], $matches)) {
 206                      for ($j = 0; $j < count($columns->columns); $j++) {
 207                          $column = $columns->columns[$j];
 208                          $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1, -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
 209                      }
 210                  }
 211              }
 212          }
 213          if (isset($id->cst)) {
 214              $contents = $id->cst->get_content();
 215              $rows = explode("\r\n", $contents);
 216              $columns = scorm_get_aicc_columns($rows[0], 'block');
 217              $regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
 218              for ($i = 1; $i < count($rows); $i++) {
 219                  if (preg_match($regexp, $rows[$i], $matches)) {
 220                      for ($j = 0; $j < count($columns->columns); $j++) {
 221                          if ($j != $columns->mastercol) {
 222                              $element = substr(trim($matches[$j + 1]), 1 , -1);
 223                              if (!empty($element)) {
 224                                  $courses[$courseid]->elements[$element]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
 225                              }
 226                          }
 227                      }
 228                  }
 229              }
 230          }
 231          if (isset($id->ort)) {
 232              $contents = $id->ort->get_content();
 233              $rows = explode("\r\n", $contents);
 234              $columns = scorm_get_aicc_columns($rows[0], 'course_element');
 235              $regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
 236              for ($i = 1; $i < count($rows); $i++) {
 237                  if (preg_match($regexp, $rows[$i], $matches)) {
 238                      for ($j = 0; $j < count($matches) - 1; $j++) {
 239                          if ($j != $columns->mastercol) {
 240                              $courses[$courseid]->elements[substr(trim($matches[$j + 1]), 1, -1)]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
 241                          }
 242                      }
 243                  }
 244              }
 245          }
 246          if (isset($id->pre)) {
 247              $contents = $id->pre->get_content();
 248              $rows = explode("\r\n", $contents);
 249              $columns = scorm_get_aicc_columns($rows[0], 'structure_element');
 250              $regexp = scorm_forge_cols_regexp($columns->columns, '(.+),');
 251              for ($i = 1; $i < count($rows); $i++) {
 252                  if (preg_match($regexp, $rows[$i], $matches)) {
 253                      $courses[$courseid]->elements[$columns->mastercol + 1]->prerequisites = substr(trim($matches[2 - $columns->mastercol]), 1, -1);
 254                  }
 255              }
 256          }
 257          if (isset($id->cmp)) {
 258              $contents = $id->cmp->get_content();
 259              $rows = explode("\r\n", $contents);
 260          }
 261      }
 262  
 263      $oldscoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id));
 264      $sortorder = 0;
 265      $launch = 0;
 266      if (isset($courses)) {
 267          foreach ($courses as $course) {
 268              $sortorder++;
 269              $sco = new stdClass();
 270              $sco->identifier = $course->id;
 271              $sco->scorm = $scorm->id;
 272              $sco->organization = '';
 273              $sco->title = $course->title;
 274              $sco->parent = '/';
 275              $sco->launch = '';
 276              $sco->scormtype = '';
 277              $sco->sortorder = $sortorder;
 278  
 279              if ($ss = $DB->get_record('scorm_scoes', array('scorm' => $scorm->id,
 280                                                             'identifier' => $sco->identifier))) {
 281                  $id = $ss->id;
 282                  $sco->id = $id;
 283                  $DB->update_record('scorm_scoes', $sco);
 284                  unset($oldscoes[$id]);
 285              } else {
 286                  $id = $DB->insert_record('scorm_scoes', $sco);
 287              }
 288  
 289              if ($launch == 0) {
 290                  $launch = $id;
 291              }
 292              if (isset($course->elements)) {
 293                  foreach ($course->elements as $element) {
 294                      unset($sco);
 295                      $sco = new stdClass();
 296                      $sco->identifier = $element->system_id;
 297                      $sco->scorm = $scorm->id;
 298                      $sco->organization = $course->id;
 299                      $sco->title = $element->title;
 300  
 301                      if (!isset($element->parent)) {
 302                          $sco->parent = '/';
 303                      } else if (strtolower($element->parent) == 'root') {
 304                          $sco->parent = $course->id;
 305                      } else {
 306                          $sco->parent = $element->parent;
 307                      }
 308                      $sco->launch = '';
 309                      $sco->scormtype = '';
 310                      $sco->previous = 0;
 311                      $sco->next = 0;
 312                      $id = null;
 313                      // Is it an Assignable Unit (AU)?
 314                      if (isset($element->file_name)) {
 315                          $sco->launch = $element->file_name;
 316                          $sco->scormtype = 'sco';
 317                      }
 318                      if ($oldscoid = scorm_array_search('identifier', $sco->identifier, $oldscoes)) {
 319                          $sco->id = $oldscoid;
 320                          $DB->update_record('scorm_scoes', $sco);
 321                          $id = $oldscoid;
 322                          $DB->delete_records('scorm_scoes_data', array('scoid' => $oldscoid));
 323                          unset($oldscoes[$oldscoid]);
 324                      } else {
 325                          $id = $DB->insert_record('scorm_scoes', $sco);
 326                      }
 327                      if (!empty($id)) {
 328                          $scodata = new stdClass();
 329                          $scodata->scoid = $id;
 330                          if (isset($element->web_launch)) {
 331                              $scodata->name = 'parameters';
 332                              $scodata->value = $element->web_launch;
 333                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 334                          }
 335                          if (isset($element->prerequisites)) {
 336                              $scodata->name = 'prerequisites';
 337                              $scodata->value = $element->prerequisites;
 338                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 339                          }
 340                          if (isset($element->max_time_allowed)) {
 341                              $scodata->name = 'max_time_allowed';
 342                              $scodata->value = $element->max_time_allowed;
 343                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 344                          }
 345                          if (isset($element->time_limit_action)) {
 346                              $scodata->name = 'time_limit_action';
 347                              $scodata->value = $element->time_limit_action;
 348                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 349                          }
 350                          if (isset($element->mastery_score)) {
 351                              $scodata->name = 'mastery_score';
 352                              $scodata->value = $element->mastery_score;
 353                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 354                          }
 355                          if (isset($element->core_vendor)) {
 356                              $scodata->name = 'datafromlms';
 357                              $scodata->value = preg_replace('/<cr>/i', "\r\n", $element->core_vendor);
 358                              $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 359                          }
 360                      }
 361                      if ($launch == 0) {
 362                          $launch = $id;
 363                      }
 364                  }
 365              }
 366          }
 367      }
 368      if (!empty($oldscoes)) {
 369          foreach ($oldscoes as $oldsco) {
 370              $DB->delete_records('scorm_scoes', array('id' => $oldsco->id));
 371              $DB->delete_records('scorm_scoes_track', array('scoid' => $oldsco->id));
 372          }
 373      }
 374  
 375      // Find first launchable object.
 376      $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
 377      // We use get_records here as we need to pass a limit in the query that works cross db.
 378      $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1);
 379      if (!empty($scoes)) {
 380          $sco = reset($scoes); // We only care about the first record - the above query only returns one.
 381          $scorm->launch = $sco->id;
 382      } else {
 383          $scorm->launch = $launch;
 384      }
 385  
 386      $scorm->version = 'AICC';
 387  
 388      return true;
 389  }
 390  
 391  /**
 392   * Given a scormid creates an AICC Session record to allow HACP
 393   *
 394   * @param int $scormid - id from scorm table
 395   * @return string hacpsession
 396   */
 397  function scorm_aicc_get_hacp_session($scormid) {
 398      global $USER, $DB, $SESSION;
 399      $cfgscorm = get_config('scorm');
 400      if (empty($cfgscorm->allowaicchacp)) {
 401          return false;
 402      }
 403      $now = time();
 404  
 405      $hacpsession = $SESSION->scorm;
 406      $hacpsession->scormid = $scormid;
 407      $hacpsession->hacpsession = random_string(20);
 408      $hacpsession->userid      = $USER->id;
 409      $hacpsession->timecreated = $now;
 410      $hacpsession->timemodified = $now;
 411      $DB->insert_record('scorm_aicc_session', $hacpsession);
 412  
 413      return $hacpsession->hacpsession;
 414  }
 415  
 416  /**
 417   * Check the hacp_session for whether it is valid.
 418   *
 419   * @param string $hacpsession The hacpsession value to check (optional). Normally leave this blank
 420   *      and this function will do required_param('sesskey', ...).
 421   * @return mixed - false if invalid, otherwise returns record from scorm_aicc_session table.
 422   */
 423  function scorm_aicc_confirm_hacp_session($hacpsession) {
 424      global $DB;
 425      $cfgscorm = get_config('scorm');
 426      if (empty($cfgscorm->allowaicchacp)) {
 427          return false;
 428      }
 429      $time = time() - ($cfgscorm->aicchacptimeout * 60);
 430      $sql = "hacpsession = ? AND timemodified > ?";
 431      $hacpsession = $DB->get_record_select('scorm_aicc_session', $sql, array($hacpsession, $time));
 432      if (!empty($hacpsession)) { // Update timemodified as this is still an active session - resets the timeout.
 433          $hacpsession->timemodified = time();
 434          $DB->update_record('scorm_aicc_session', $hacpsession);
 435      }
 436      return $hacpsession;
 437  }
 438  
 439  /**
 440   * generate a simple single activity AICC object
 441   * structure to wrap around and externally linked
 442   * AICC package URL
 443   *
 444   * @param object $scorm package record
 445   */
 446  function scorm_aicc_generate_simple_sco($scorm) {
 447      global $DB;
 448      // Find the oldest one.
 449      $scos = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'id');
 450      if (!empty($scos)) {
 451          $sco = array_shift($scos);
 452      } else {
 453          $sco = new stdClass();
 454      }
 455      // Get rid of old ones.
 456      foreach ($scos as $oldsco) {
 457          $DB->delete_records('scorm_scoes', array('id' => $oldsco->id));
 458          $DB->delete_records('scorm_scoes_track', array('scoid' => $oldsco->id));
 459      }
 460  
 461      $sco->identifier = 'A1';
 462      $sco->scorm = $scorm->id;
 463      $sco->organization = '';
 464      $sco->title = $scorm->name;
 465      $sco->parent = '/';
 466      // Add the HACP signal to the activity launcher.
 467      if (preg_match('/\?/', $scorm->reference)) {
 468          $sco->launch = $scorm->reference.'&CMI=HACP';
 469      } else {
 470          $sco->launch = $scorm->reference.'?CMI=HACP';
 471      }
 472      $sco->scormtype = 'sco';
 473      if (isset($sco->id)) {
 474          $DB->update_record('scorm_scoes', $sco);
 475          $id = $sco->id;
 476      } else {
 477          $id = $DB->insert_record('scorm_scoes', $sco);
 478      }
 479      return $id;
 480  }
 481  
 482  /**
 483   * Sets up $userdata array and default values for AICC package.
 484   *
 485   * @param stdClass $userdata an empty stdClass variable that should be set up with user values
 486   * @param object $scorm package record
 487   * @param string $scoid SCO Id
 488   * @param string $attempt attempt number for the user
 489   * @param string $mode scorm display mode type
 490   * @return array The default values that should be used for AICC package
 491   */
 492  function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {
 493      global $USER;
 494      $aiccuserid = get_config('scorm', 'aiccuserid');
 495      if (!empty($aiccuserid)) {
 496          $userdata->student_id = $USER->id;
 497      } else {
 498          $userdata->student_id = $USER->username;
 499      }
 500      $userdata->student_name = $USER->lastname .', '. $USER->firstname;
 501  
 502      if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {
 503          foreach ($usertrack as $key => $value) {
 504              $userdata->$key = $value;
 505          }
 506      } else {
 507          $userdata->status = '';
 508          $userdata->score_raw = '';
 509      }
 510  
 511      if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
 512          foreach ($scodatas as $key => $value) {
 513              $userdata->$key = $value;
 514          }
 515      } else {
 516          print_error('cannotfindsco', 'scorm');
 517      }
 518      if (!$sco = scorm_get_sco($scoid)) {
 519          print_error('cannotfindsco', 'scorm');
 520      }
 521  
 522      $userdata->mode = 'normal';
 523      if (!empty($mode)) {
 524          $userdata->mode = $mode;
 525      }
 526      if ($userdata->mode == 'normal') {
 527          $userdata->credit = 'credit';
 528      } else {
 529          $userdata->credit = 'no-credit';
 530      }
 531  
 532      if (isset($userdata->status)) {
 533          if ($userdata->status == '') {
 534              $userdata->entry = 'ab-initio';
 535          } else {
 536              if (isset($userdata->{'cmi.core.exit'}) && ($userdata->{'cmi.core.exit'} == 'suspend')) {
 537                  $userdata->entry = 'resume';
 538              } else {
 539                  $userdata->entry = '';
 540              }
 541          }
 542      }
 543  
 544      $def = array();
 545      $def['cmi.core.student_id'] = $userdata->student_id;
 546      $def['cmi.core.student_name'] = $userdata->student_name;
 547      $def['cmi.core.credit'] = $userdata->credit;
 548      $def['cmi.core.entry'] = $userdata->entry;
 549      $def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');
 550      $def['cmi.core.lesson_mode'] = $userdata->mode;
 551      $def['cmi.student_data.attempt_number'] = scorm_isset($userdata, 'cmi.student_data.attempt_number');
 552      $def['cmi.student_data.mastery_score'] = scorm_isset($userdata, 'mastery_score');
 553      $def['cmi.student_data.max_time_allowed'] = scorm_isset($userdata, 'max_time_allowed');
 554      $def['cmi.student_data.time_limit_action'] = scorm_isset($userdata, 'time_limit_action');
 555      $def['cmi.student_data.tries_during_lesson'] = scorm_isset($userdata, 'cmi.student_data.tries_during_lesson');
 556  
 557      $def['cmi.core.lesson_location'] = scorm_isset($userdata, 'cmi.core.lesson_location');
 558      $def['cmi.core.lesson_status'] = scorm_isset($userdata, 'cmi.core.lesson_status');
 559      $def['cmi.core.exit'] = scorm_isset($userdata, 'cmi.core.exit');
 560      $def['cmi.core.score.raw'] = scorm_isset($userdata, 'cmi.core.score.raw');
 561      $def['cmi.core.score.max'] = scorm_isset($userdata, 'cmi.core.score.max');
 562      $def['cmi.core.score.min'] = scorm_isset($userdata, 'cmi.core.score.min');
 563      $def['cmi.core.total_time'] = scorm_isset($userdata, 'cmi.core.total_time', '00:00:00');
 564      $def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');
 565      $def['cmi.comments'] = scorm_isset($userdata, 'cmi.comments');
 566  
 567      return $def;
 568  }


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