Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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   * SCORM module external API
  19   *
  20   * @package    mod_scorm
  21   * @category   external
  22   * @copyright  2015 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 3.0
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  require_once($CFG->libdir . '/externallib.php');
  30  require_once($CFG->dirroot . '/mod/scorm/lib.php');
  31  require_once($CFG->dirroot . '/mod/scorm/locallib.php');
  32  
  33  /**
  34   * SCORM module external functions
  35   *
  36   * @package    mod_scorm
  37   * @category   external
  38   * @copyright  2015 Juan Leyva <juan@moodle.com>
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   * @since      Moodle 3.0
  41   */
  42  class mod_scorm_external extends external_api {
  43  
  44      /**
  45       * Returns description of method parameters
  46       *
  47       * @return external_function_parameters
  48       * @since Moodle 3.0
  49       */
  50      public static function view_scorm_parameters() {
  51          return new external_function_parameters(
  52              array(
  53                  'scormid' => new external_value(PARAM_INT, 'scorm instance id')
  54              )
  55          );
  56      }
  57  
  58      /**
  59       * Trigger the course module viewed event.
  60       *
  61       * @param int $scormid the scorm instance id
  62       * @return array of warnings and status result
  63       * @since Moodle 3.0
  64       * @throws moodle_exception
  65       */
  66      public static function view_scorm($scormid) {
  67          global $DB, $CFG;
  68          require_once($CFG->dirroot . '/mod/scorm/lib.php');
  69  
  70          $params = self::validate_parameters(self::view_scorm_parameters(),
  71                                              array(
  72                                                  'scormid' => $scormid
  73                                              ));
  74          $warnings = array();
  75  
  76          // Request and permission validation.
  77          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
  78          list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
  79  
  80          $context = context_module::instance($cm->id);
  81          self::validate_context($context);
  82  
  83          // Call the scorm/lib API.
  84          scorm_view($scorm, $course, $cm, $context);
  85  
  86          $result = array();
  87          $result['status'] = true;
  88          $result['warnings'] = $warnings;
  89          return $result;
  90      }
  91  
  92      /**
  93       * Returns description of method result value
  94       *
  95       * @return external_description
  96       * @since Moodle 3.0
  97       */
  98      public static function view_scorm_returns() {
  99          return new external_single_structure(
 100              array(
 101                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
 102                  'warnings' => new external_warnings()
 103              )
 104          );
 105      }
 106  
 107      /**
 108       * Describes the parameters for get_scorm_attempt_count.
 109       *
 110       * @return external_function_parameters
 111       * @since Moodle 3.0
 112       */
 113      public static function get_scorm_attempt_count_parameters() {
 114          return new external_function_parameters(
 115              array(
 116                  'scormid' => new external_value(PARAM_INT, 'SCORM instance id'),
 117                  'userid' => new external_value(PARAM_INT, 'User id'),
 118                  'ignoremissingcompletion' => new external_value(PARAM_BOOL,
 119                                                  'Ignores attempts that haven\'t reported a grade/completion',
 120                                                  VALUE_DEFAULT, false),
 121              )
 122          );
 123      }
 124  
 125      /**
 126       * Return the number of attempts done by a user in the given SCORM.
 127       *
 128       * @param int $scormid the scorm id
 129       * @param int $userid the user id
 130       * @param bool $ignoremissingcompletion ignores attempts that haven't reported a grade/completion
 131       * @return array of warnings and the attempts count
 132       * @since Moodle 3.0
 133       */
 134      public static function get_scorm_attempt_count($scormid, $userid, $ignoremissingcompletion = false) {
 135          global $USER, $DB;
 136  
 137          $params = self::validate_parameters(self::get_scorm_attempt_count_parameters(),
 138                                              array('scormid' => $scormid, 'userid' => $userid,
 139                                                  'ignoremissingcompletion' => $ignoremissingcompletion));
 140  
 141          $attempts = array();
 142          $warnings = array();
 143  
 144          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 145          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 146  
 147          $context = context_module::instance($cm->id);
 148          self::validate_context($context);
 149  
 150          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 151          core_user::require_active_user($user);
 152  
 153          // Extra checks so only users with permissions can view other users attempts.
 154          if ($USER->id != $user->id) {
 155              require_capability('mod/scorm:viewreport', $context);
 156          }
 157  
 158          // If the SCORM is not open this function will throw exceptions.
 159          scorm_require_available($scorm);
 160  
 161          $attemptscount = scorm_get_attempt_count($user->id, $scorm, false, $params['ignoremissingcompletion']);
 162  
 163          $result = array();
 164          $result['attemptscount'] = $attemptscount;
 165          $result['warnings'] = $warnings;
 166          return $result;
 167      }
 168  
 169      /**
 170       * Describes the get_scorm_attempt_count return value.
 171       *
 172       * @return external_single_structure
 173       * @since Moodle 3.0
 174       */
 175      public static function get_scorm_attempt_count_returns() {
 176  
 177          return new external_single_structure(
 178              array(
 179                  'attemptscount' => new external_value(PARAM_INT, 'Attempts count'),
 180                  'warnings' => new external_warnings(),
 181              )
 182          );
 183      }
 184  
 185      /**
 186       * Describes the parameters for get_scorm_scoes.
 187       *
 188       * @return external_function_parameters
 189       * @since Moodle 3.0
 190       */
 191      public static function get_scorm_scoes_parameters() {
 192          return new external_function_parameters(
 193              array(
 194                  'scormid' => new external_value(PARAM_INT, 'scorm instance id'),
 195                  'organization' => new external_value(PARAM_RAW, 'organization id', VALUE_DEFAULT, '')
 196              )
 197          );
 198      }
 199  
 200      /**
 201       * Returns a list containing all the scoes data related to the given scorm id
 202       *
 203       * @param int $scormid the scorm id
 204       * @param string $organization the organization id
 205       * @return array warnings and the scoes data
 206       * @since Moodle 3.0
 207       */
 208      public static function get_scorm_scoes($scormid, $organization = '') {
 209          global $DB;
 210  
 211          $params = self::validate_parameters(self::get_scorm_scoes_parameters(),
 212                                              array('scormid' => $scormid, 'organization' => $organization));
 213  
 214          $scoes = array();
 215          $warnings = array();
 216  
 217          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 218          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 219  
 220          $context = context_module::instance($cm->id);
 221          self::validate_context($context);
 222  
 223          // Check settings / permissions to view the SCORM.
 224          scorm_require_available($scorm, true, $context);
 225  
 226          if (!$scoes = scorm_get_scoes($scorm->id, $params['organization'])) {
 227              // Function scorm_get_scoes return false, not an empty array.
 228              $scoes = array();
 229          } else {
 230              $scoreturnstructure = self::get_scorm_scoes_returns();
 231              foreach ($scoes as $sco) {
 232                  $extradata = array();
 233                  foreach ($sco as $element => $value) {
 234                      // Check if the element is extra data (not a basic SCO element).
 235                      if (!isset($scoreturnstructure->keys['scoes']->content->keys[$element])) {
 236                          $extradata[] = array(
 237                              'element' => $element,
 238                              'value' => $value
 239                          );
 240                      }
 241                  }
 242                  $sco->extradata = $extradata;
 243              }
 244          }
 245  
 246          $result = array();
 247          $result['scoes'] = $scoes;
 248          $result['warnings'] = $warnings;
 249          return $result;
 250      }
 251  
 252      /**
 253       * Describes the get_scorm_scoes return value.
 254       *
 255       * @return external_single_structure
 256       * @since Moodle 3.0
 257       */
 258      public static function get_scorm_scoes_returns() {
 259  
 260          return new external_single_structure(
 261              array(
 262                  'scoes' => new external_multiple_structure(
 263                      new external_single_structure(
 264                          array(
 265                              'id' => new external_value(PARAM_INT, 'sco id'),
 266                              'scorm' => new external_value(PARAM_INT, 'scorm id'),
 267                              'manifest' => new external_value(PARAM_NOTAGS, 'manifest id'),
 268                              'organization' => new external_value(PARAM_NOTAGS, 'organization id'),
 269                              'parent' => new external_value(PARAM_NOTAGS, 'parent'),
 270                              'identifier' => new external_value(PARAM_NOTAGS, 'identifier'),
 271                              'launch' => new external_value(PARAM_NOTAGS, 'launch file'),
 272                              'scormtype' => new external_value(PARAM_ALPHA, 'scorm type (asset, sco)'),
 273                              'title' => new external_value(PARAM_NOTAGS, 'sco title'),
 274                              'sortorder' => new external_value(PARAM_INT, 'sort order'),
 275                              'extradata' => new external_multiple_structure(
 276                                  new external_single_structure(
 277                                      array(
 278                                          'element' => new external_value(PARAM_RAW, 'element name'),
 279                                          'value' => new external_value(PARAM_RAW, 'element value')
 280                                      )
 281                                  ), 'Additional SCO data', VALUE_OPTIONAL
 282                              )
 283                          ), 'SCORM SCO data'
 284                      )
 285                  ),
 286                  'warnings' => new external_warnings(),
 287              )
 288          );
 289      }
 290  
 291      /**
 292       * Describes the parameters for get_scorm_user_data.
 293       *
 294       * @return external_function_parameters
 295       * @since Moodle 3.0
 296       */
 297      public static function get_scorm_user_data_parameters() {
 298          return new external_function_parameters(
 299              array(
 300                  'scormid' => new external_value(PARAM_INT, 'scorm instance id'),
 301                  'attempt' => new external_value(PARAM_INT, 'attempt number')
 302              )
 303          );
 304      }
 305  
 306      /**
 307       * Retrieves user tracking and SCO data and default SCORM values
 308       *
 309       * @param int $scormid the scorm id
 310       * @param int $attempt the attempt number
 311       * @return array warnings and the scoes data
 312       * @throws  moodle_exception
 313       * @since Moodle 3.0
 314       */
 315      public static function get_scorm_user_data($scormid, $attempt) {
 316          global $CFG, $DB;
 317  
 318          $params = self::validate_parameters(self::get_scorm_user_data_parameters(),
 319                                              array('scormid' => $scormid, 'attempt' => $attempt));
 320  
 321          $data = array();
 322          $warnings = array();
 323  
 324          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 325          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 326  
 327          $context = context_module::instance($cm->id);
 328          self::validate_context($context);
 329  
 330          scorm_require_available($scorm, true, $context);
 331  
 332          $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR));
 333          if (!file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php')) {
 334              $scorm->version = 'scorm_12';
 335          }
 336          require_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php');
 337  
 338          if ($scoes = scorm_get_scoes($scorm->id)) {
 339              $def = new stdClass();
 340              $user = new stdClass();
 341  
 342              foreach ($scoes as $sco) {
 343                  $def->{$sco->id} = new stdClass();
 344                  $user->{$sco->id} = new stdClass();
 345                  // We force mode normal, this can be override by the client at any time.
 346                  $def->{$sco->id} = get_scorm_default($user->{$sco->id}, $scorm, $sco->id, $params['attempt'], 'normal');
 347  
 348                  $userdata = array();
 349                  $defaultdata = array();
 350  
 351                  foreach ((array) $user->{$sco->id} as $key => $val) {
 352                      $userdata[] = array(
 353                          'element' => $key,
 354                          'value' => $val
 355                      );
 356                  }
 357                  foreach ($def->{$sco->id} as $key => $val) {
 358                      $defaultdata[] = array(
 359                          'element' => $key,
 360                          'value' => $val
 361                      );
 362                  }
 363  
 364                  $data[] = array(
 365                      'scoid' => $sco->id,
 366                      'userdata' => $userdata,
 367                      'defaultdata' => $defaultdata,
 368                  );
 369              }
 370          }
 371  
 372          $result = array();
 373          $result['data'] = $data;
 374          $result['warnings'] = $warnings;
 375          return $result;
 376      }
 377  
 378      /**
 379       * Describes the get_scorm_user_data return value.
 380       *
 381       * @return external_single_structure
 382       * @since Moodle 3.0
 383       */
 384      public static function get_scorm_user_data_returns() {
 385  
 386          return new external_single_structure(
 387              array(
 388                  'data' => new external_multiple_structure(
 389                      new external_single_structure(
 390                          array(
 391                              'scoid' => new external_value(PARAM_INT, 'sco id'),
 392                              'userdata' => new external_multiple_structure(
 393                                              new external_single_structure(
 394                                                  array(
 395                                                      'element' => new external_value(PARAM_RAW, 'element name'),
 396                                                      'value' => new external_value(PARAM_RAW, 'element value')
 397                                                  )
 398                                              )
 399                                            ),
 400                              'defaultdata' => new external_multiple_structure(
 401                                                  new external_single_structure(
 402                                                      array(
 403                                                          'element' => new external_value(PARAM_RAW, 'element name'),
 404                                                          'value' => new external_value(PARAM_RAW, 'element value')
 405                                                      )
 406                                                  )
 407                                               ),
 408                          ), 'SCO data'
 409                      )
 410                  ),
 411                  'warnings' => new external_warnings(),
 412              )
 413          );
 414      }
 415  
 416      /**
 417       * Describes the parameters for insert_scorm_tracks.
 418       *
 419       * @return external_function_parameters
 420       * @since Moodle 3.0
 421       */
 422      public static function insert_scorm_tracks_parameters() {
 423          return new external_function_parameters(
 424              array(
 425                  'scoid' => new external_value(PARAM_INT, 'SCO id'),
 426                  'attempt' => new external_value(PARAM_INT, 'attempt number'),
 427                  'tracks' => new external_multiple_structure(
 428                      new external_single_structure(
 429                          array(
 430                              'element' => new external_value(PARAM_RAW, 'element name'),
 431                              'value' => new external_value(PARAM_RAW, 'element value')
 432                          )
 433                      )
 434                  ),
 435              )
 436          );
 437      }
 438  
 439      /**
 440       * Saves a SCORM tracking record.
 441       * It will overwrite any existing tracking data for this attempt.
 442       * Validation should be performed before running the function to ensure the user will not lose any existing attempt data.
 443       *
 444       * @param int $scoid the SCO id
 445       * @param string $attempt the attempt number
 446       * @param array $tracks the track records to be stored
 447       * @return array warnings and the scoes data
 448       * @throws moodle_exception
 449       * @since Moodle 3.0
 450       */
 451      public static function insert_scorm_tracks($scoid, $attempt, $tracks) {
 452          global $USER, $DB;
 453  
 454          $params = self::validate_parameters(self::insert_scorm_tracks_parameters(),
 455                                              array('scoid' => $scoid, 'attempt' => $attempt, 'tracks' => $tracks));
 456  
 457          $trackids = array();
 458          $warnings = array();
 459  
 460          $sco = scorm_get_sco($params['scoid'], SCO_ONLY);
 461          if (!$sco) {
 462              throw new moodle_exception('cannotfindsco', 'scorm');
 463          }
 464  
 465          $scorm = $DB->get_record('scorm', array('id' => $sco->scorm), '*', MUST_EXIST);
 466          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 467  
 468          $context = context_module::instance($cm->id);
 469          self::validate_context($context);
 470  
 471          // Check settings / permissions to view the SCORM.
 472          require_capability('mod/scorm:savetrack', $context);
 473  
 474          // Check settings / permissions to view the SCORM.
 475          scorm_require_available($scorm);
 476  
 477          foreach ($params['tracks'] as $track) {
 478              $element = $track['element'];
 479              $value = $track['value'];
 480              $trackid = scorm_insert_track($USER->id, $scorm->id, $sco->id, $params['attempt'], $element, $value,
 481                                              $scorm->forcecompleted);
 482  
 483              if ($trackid) {
 484                  $trackids[] = $trackid;
 485              } else {
 486                  $warnings[] = array(
 487                      'item' => 'scorm',
 488                      'itemid' => $scorm->id,
 489                      'warningcode' => 1,
 490                      'message' => 'Element: ' . $element . ' was not saved'
 491                  );
 492              }
 493          }
 494  
 495          $result = array();
 496          $result['trackids'] = $trackids;
 497          $result['warnings'] = $warnings;
 498          return $result;
 499      }
 500  
 501      /**
 502       * Describes the insert_scorm_tracks return value.
 503       *
 504       * @return external_single_structure
 505       * @since Moodle 3.0
 506       */
 507      public static function insert_scorm_tracks_returns() {
 508  
 509          return new external_single_structure(
 510              array(
 511                  'trackids' => new external_multiple_structure(new external_value(PARAM_INT, 'track id')),
 512                  'warnings' => new external_warnings(),
 513              )
 514          );
 515      }
 516  
 517      /**
 518       * Describes the parameters for get_scorm_sco_tracks.
 519       *
 520       * @return external_function_parameters
 521       * @since Moodle 3.0
 522       */
 523      public static function get_scorm_sco_tracks_parameters() {
 524          return new external_function_parameters(
 525              array(
 526                  'scoid' => new external_value(PARAM_INT, 'sco id'),
 527                  'userid' => new external_value(PARAM_INT, 'user id'),
 528                  'attempt' => new external_value(PARAM_INT, 'attempt number (0 for last attempt)', VALUE_DEFAULT, 0)
 529              )
 530          );
 531      }
 532  
 533      /**
 534       * Retrieves SCO tracking data for the given user id and attempt number
 535       *
 536       * @param int $scoid the sco id
 537       * @param int $userid the user id
 538       * @param int $attempt the attempt number
 539       * @return array warnings and the scoes data
 540       * @since Moodle 3.0
 541       */
 542      public static function get_scorm_sco_tracks($scoid, $userid, $attempt = 0) {
 543          global $USER, $DB;
 544  
 545          $params = self::validate_parameters(self::get_scorm_sco_tracks_parameters(),
 546                                              array('scoid' => $scoid, 'userid' => $userid, 'attempt' => $attempt));
 547  
 548          $tracks = array();
 549          $warnings = array();
 550  
 551          $sco = scorm_get_sco($params['scoid'], SCO_ONLY);
 552          if (!$sco) {
 553              throw new moodle_exception('cannotfindsco', 'scorm');
 554          }
 555  
 556          $scorm = $DB->get_record('scorm', array('id' => $sco->scorm), '*', MUST_EXIST);
 557          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 558  
 559          $context = context_module::instance($cm->id);
 560          self::validate_context($context);
 561  
 562          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 563          core_user::require_active_user($user);
 564  
 565          // Extra checks so only users with permissions can view other users attempts.
 566          if ($USER->id != $user->id) {
 567              require_capability('mod/scorm:viewreport', $context);
 568          }
 569  
 570          scorm_require_available($scorm, true, $context);
 571  
 572          if (empty($params['attempt'])) {
 573              $params['attempt'] = scorm_get_last_attempt($scorm->id, $user->id);
 574          }
 575  
 576          $attempted = false;
 577          if ($scormtracks = scorm_get_tracks($sco->id, $params['userid'], $params['attempt'])) {
 578              // Check if attempted.
 579              if ($scormtracks->status != '') {
 580                  $attempted = true;
 581                  foreach ($scormtracks as $element => $value) {
 582                      $tracks[] = array(
 583                          'element' => $element,
 584                          'value' => $value,
 585                      );
 586                  }
 587              }
 588          }
 589  
 590          if (!$attempted) {
 591              $warnings[] = array(
 592                  'item' => 'attempt',
 593                  'itemid' => $params['attempt'],
 594                  'warningcode' => 'notattempted',
 595                  'message' => get_string('notattempted', 'scorm')
 596              );
 597          }
 598  
 599          $result = array();
 600          $result['data']['attempt'] = $params['attempt'];
 601          $result['data']['tracks'] = $tracks;
 602          $result['warnings'] = $warnings;
 603          return $result;
 604      }
 605  
 606      /**
 607       * Describes the get_scorm_sco_tracks return value.
 608       *
 609       * @return external_single_structure
 610       * @since Moodle 3.0
 611       */
 612      public static function get_scorm_sco_tracks_returns() {
 613  
 614          return new external_single_structure(
 615              array(
 616                  'data' => new external_single_structure(
 617                      array(
 618                          'attempt' => new external_value(PARAM_INT, 'Attempt number'),
 619                          'tracks' => new external_multiple_structure(
 620                              new external_single_structure(
 621                                  array(
 622                                      'element' => new external_value(PARAM_RAW, 'Element name'),
 623                                      'value' => new external_value(PARAM_RAW, 'Element value')
 624                                  ), 'Tracks data'
 625                              )
 626                          ),
 627                      ), 'SCO data'
 628                  ),
 629                  'warnings' => new external_warnings(),
 630              )
 631          );
 632      }
 633  
 634      /**
 635       * Describes the parameters for get_scorms_by_courses.
 636       *
 637       * @return external_function_parameters
 638       * @since Moodle 3.0
 639       */
 640      public static function get_scorms_by_courses_parameters() {
 641          return new external_function_parameters (
 642              array(
 643                  'courseids' => new external_multiple_structure(
 644                      new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array()
 645                  ),
 646              )
 647          );
 648      }
 649  
 650      /**
 651       * Returns a list of scorms in a provided list of courses,
 652       * if no list is provided all scorms that the user can view will be returned.
 653       *
 654       * @param array $courseids the course ids
 655       * @return array the scorm details
 656       * @since Moodle 3.0
 657       */
 658      public static function get_scorms_by_courses($courseids = array()) {
 659          global $CFG;
 660  
 661          $returnedscorms = array();
 662          $warnings = array();
 663  
 664          $params = self::validate_parameters(self::get_scorms_by_courses_parameters(), array('courseids' => $courseids));
 665  
 666          $courses = array();
 667          if (empty($params['courseids'])) {
 668              $courses = enrol_get_my_courses();
 669              $params['courseids'] = array_keys($courses);
 670          }
 671  
 672          // Ensure there are courseids to loop through.
 673          if (!empty($params['courseids'])) {
 674  
 675              list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 676  
 677              // Get the scorms in this course, this function checks users visibility permissions.
 678              // We can avoid then additional validate_context calls.
 679              $scorms = get_all_instances_in_courses("scorm", $courses);
 680  
 681              $fs = get_file_storage();
 682              foreach ($scorms as $scorm) {
 683  
 684                  $context = context_module::instance($scorm->coursemodule);
 685  
 686                  // Entry to return.
 687                  $module = array();
 688  
 689                  // First, we return information that any user can see in (or can deduce from) the web interface.
 690                  $module['id'] = $scorm->id;
 691                  $module['coursemodule'] = $scorm->coursemodule;
 692                  $module['course'] = $scorm->course;
 693                  $module['name']  = external_format_string($scorm->name, $context->id);
 694                  $options = array('noclean' => true);
 695                  list($module['intro'], $module['introformat']) =
 696                      external_format_text($scorm->intro, $scorm->introformat, $context->id, 'mod_scorm', 'intro', null, $options);
 697                  $module['introfiles'] = external_util::get_area_files($context->id, 'mod_scorm', 'intro', false, false);
 698  
 699                  // Check if the SCORM open and return warnings if so.
 700                  list($open, $openwarnings) = scorm_get_availability_status($scorm, true, $context);
 701  
 702                  if (!$open) {
 703                      foreach ($openwarnings as $warningkey => $warningdata) {
 704                          $warnings[] = array(
 705                              'item' => 'scorm',
 706                              'itemid' => $scorm->id,
 707                              'warningcode' => $warningkey,
 708                              'message' => get_string($warningkey, 'scorm', $warningdata)
 709                          );
 710                      }
 711                  } else {
 712                      $module['packagesize'] = 0;
 713                      // SCORM size.
 714                      if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) {
 715                          if ($packagefile = $fs->get_file($context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)) {
 716                              $module['packagesize'] = $packagefile->get_filesize();
 717                              // Download URL.
 718                              $module['packageurl'] = moodle_url::make_webservice_pluginfile_url(
 719                                                      $context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)->out(false);
 720                          }
 721                      }
 722  
 723                      $module['protectpackagedownloads'] = get_config('scorm', 'protectpackagedownloads');
 724  
 725                      $viewablefields = array('version', 'maxgrade', 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted',
 726                                              'forcenewattempt', 'lastattemptlock', 'displayattemptstatus', 'displaycoursestructure',
 727                                              'sha1hash', 'md5hash', 'revision', 'launch', 'skipview', 'hidebrowse', 'hidetoc', 'nav',
 728                                              'navpositionleft', 'navpositiontop', 'auto', 'popup', 'width', 'height', 'timeopen',
 729                                              'timeclose', 'displayactivityname', 'scormtype', 'reference');
 730  
 731                      // Check additional permissions for returning optional private settings.
 732                      if (has_capability('moodle/course:manageactivities', $context)) {
 733  
 734                          $additionalfields = array('updatefreq', 'options', 'completionstatusrequired', 'completionscorerequired',
 735                                                    'completionstatusallscos', 'autocommit', 'timemodified', 'section', 'visible',
 736                                                    'groupmode', 'groupingid');
 737                          $viewablefields = array_merge($viewablefields, $additionalfields);
 738  
 739                      }
 740  
 741                      foreach ($viewablefields as $field) {
 742                          $module[$field] = $scorm->{$field};
 743                      }
 744                  }
 745  
 746                  $returnedscorms[] = $module;
 747              }
 748          }
 749  
 750          $result = array();
 751          $result['scorms'] = $returnedscorms;
 752          $result['warnings'] = $warnings;
 753          return $result;
 754      }
 755  
 756      /**
 757       * Describes the get_scorms_by_courses return value.
 758       *
 759       * @return external_single_structure
 760       * @since Moodle 3.0
 761       */
 762      public static function get_scorms_by_courses_returns() {
 763  
 764          return new external_single_structure(
 765              array(
 766                  'scorms' => new external_multiple_structure(
 767                      new external_single_structure(
 768                          array(
 769                              'id' => new external_value(PARAM_INT, 'SCORM id'),
 770                              'coursemodule' => new external_value(PARAM_INT, 'Course module id'),
 771                              'course' => new external_value(PARAM_INT, 'Course id'),
 772                              'name' => new external_value(PARAM_RAW, 'SCORM name'),
 773                              'intro' => new external_value(PARAM_RAW, 'The SCORM intro'),
 774                              'introformat' => new external_format_value('intro'),
 775                              'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
 776                              'packagesize' => new external_value(PARAM_INT, 'SCORM zip package size', VALUE_OPTIONAL),
 777                              'packageurl' => new external_value(PARAM_URL, 'SCORM zip package URL', VALUE_OPTIONAL),
 778                              'version' => new external_value(PARAM_NOTAGS, 'SCORM version (SCORM_12, SCORM_13, SCORM_AICC)',
 779                                                              VALUE_OPTIONAL),
 780                              'maxgrade' => new external_value(PARAM_INT, 'Max grade', VALUE_OPTIONAL),
 781                              'grademethod' => new external_value(PARAM_INT, 'Grade method', VALUE_OPTIONAL),
 782                              'whatgrade' => new external_value(PARAM_INT, 'What grade', VALUE_OPTIONAL),
 783                              'maxattempt' => new external_value(PARAM_INT, 'Maximum number of attemtps', VALUE_OPTIONAL),
 784                              'forcecompleted' => new external_value(PARAM_BOOL, 'Status current attempt is forced to "completed"',
 785                                                                      VALUE_OPTIONAL),
 786                              'forcenewattempt' => new external_value(PARAM_INT, 'Controls re-entry behaviour',
 787                                                                      VALUE_OPTIONAL),
 788                              'lastattemptlock' => new external_value(PARAM_BOOL, 'Prevents to launch new attempts once finished',
 789                                                                      VALUE_OPTIONAL),
 790                              'displayattemptstatus' => new external_value(PARAM_INT, 'How to display attempt status',
 791                                                                              VALUE_OPTIONAL),
 792                              'displaycoursestructure' => new external_value(PARAM_BOOL, 'Display contents structure',
 793                                                                              VALUE_OPTIONAL),
 794                              'sha1hash' => new external_value(PARAM_NOTAGS, 'Package content or ext path hash', VALUE_OPTIONAL),
 795                              'md5hash' => new external_value(PARAM_NOTAGS, 'MD5 Hash of package file', VALUE_OPTIONAL),
 796                              'revision' => new external_value(PARAM_INT, 'Revison number', VALUE_OPTIONAL),
 797                              'launch' => new external_value(PARAM_INT, 'First content to launch', VALUE_OPTIONAL),
 798                              'skipview' => new external_value(PARAM_INT, 'How to skip the content structure page', VALUE_OPTIONAL),
 799                              'hidebrowse' => new external_value(PARAM_BOOL, 'Disable preview mode?', VALUE_OPTIONAL),
 800                              'hidetoc' => new external_value(PARAM_INT, 'How to display the SCORM structure in player',
 801                                                              VALUE_OPTIONAL),
 802                              'nav' => new external_value(PARAM_INT, 'Show navigation buttons', VALUE_OPTIONAL),
 803                              'navpositionleft' => new external_value(PARAM_INT, 'Navigation position left', VALUE_OPTIONAL),
 804                              'navpositiontop' => new external_value(PARAM_INT, 'Navigation position top', VALUE_OPTIONAL),
 805                              'auto' => new external_value(PARAM_BOOL, 'Auto continue?', VALUE_OPTIONAL),
 806                              'popup' => new external_value(PARAM_INT, 'Display in current or new window', VALUE_OPTIONAL),
 807                              'width' => new external_value(PARAM_INT, 'Frame width', VALUE_OPTIONAL),
 808                              'height' => new external_value(PARAM_INT, 'Frame height', VALUE_OPTIONAL),
 809                              'timeopen' => new external_value(PARAM_INT, 'Available from', VALUE_OPTIONAL),
 810                              'timeclose' => new external_value(PARAM_INT, 'Available to', VALUE_OPTIONAL),
 811                              'displayactivityname' => new external_value(PARAM_BOOL, 'Display the activity name above the player?',
 812                                                                          VALUE_OPTIONAL),
 813                              'scormtype' => new external_value(PARAM_ALPHA, 'SCORM type', VALUE_OPTIONAL),
 814                              'reference' => new external_value(PARAM_NOTAGS, 'Reference to the package', VALUE_OPTIONAL),
 815                              'protectpackagedownloads' => new external_value(PARAM_BOOL, 'Protect package downloads?',
 816                                                                              VALUE_OPTIONAL),
 817                              'updatefreq' => new external_value(PARAM_INT, 'Auto-update frequency for remote packages',
 818                                                                  VALUE_OPTIONAL),
 819                              'options' => new external_value(PARAM_RAW, 'Additional options', VALUE_OPTIONAL),
 820                              'completionstatusrequired' => new external_value(PARAM_INT, 'Status passed/completed required?',
 821                                                                                  VALUE_OPTIONAL),
 822                              'completionscorerequired' => new external_value(PARAM_INT, 'Minimum score required', VALUE_OPTIONAL),
 823                              'completionstatusallscos' => new external_value(PARAM_INT, 'Require all scos to return completion status', VALUE_OPTIONAL),
 824                              'autocommit' => new external_value(PARAM_BOOL, 'Save track data automatically?', VALUE_OPTIONAL),
 825                              'timemodified' => new external_value(PARAM_INT, 'Time of last modification', VALUE_OPTIONAL),
 826                              'section' => new external_value(PARAM_INT, 'Course section id', VALUE_OPTIONAL),
 827                              'visible' => new external_value(PARAM_BOOL, 'Visible', VALUE_OPTIONAL),
 828                              'groupmode' => new external_value(PARAM_INT, 'Group mode', VALUE_OPTIONAL),
 829                              'groupingid' => new external_value(PARAM_INT, 'Group id', VALUE_OPTIONAL),
 830                          ), 'SCORM'
 831                      )
 832                  ),
 833                  'warnings' => new external_warnings(),
 834              )
 835          );
 836      }
 837  
 838      /**
 839       * Returns description of method parameters
 840       *
 841       * @return external_function_parameters
 842       * @since Moodle 3.1
 843       */
 844      public static function launch_sco_parameters() {
 845          return new external_function_parameters(
 846              array(
 847                  'scormid' => new external_value(PARAM_INT, 'SCORM instance id'),
 848                  'scoid' => new external_value(PARAM_INT, 'SCO id (empty for launching the first SCO)', VALUE_DEFAULT, 0)
 849              )
 850          );
 851      }
 852  
 853      /**
 854       * Trigger the course module viewed event.
 855       *
 856       * @param int $scormid the SCORM instance id
 857       * @param int $scoid the SCO id
 858       * @return array of warnings and status result
 859       * @since Moodle 3.1
 860       * @throws moodle_exception
 861       */
 862      public static function launch_sco($scormid, $scoid = 0) {
 863          global $DB, $CFG;
 864  
 865          require_once($CFG->libdir . '/completionlib.php');
 866  
 867          $params = self::validate_parameters(self::launch_sco_parameters(),
 868                                              array(
 869                                                  'scormid' => $scormid,
 870                                                  'scoid' => $scoid
 871                                              ));
 872          $warnings = array();
 873  
 874          // Request and permission validation.
 875          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 876          list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
 877  
 878          $context = context_module::instance($cm->id);
 879          self::validate_context($context);
 880  
 881          // If the SCORM is not open this function will throw exceptions.
 882          scorm_require_available($scorm);
 883  
 884          if (!empty($params['scoid']) and !($sco = scorm_get_sco($params['scoid'], SCO_ONLY))) {
 885              throw new moodle_exception('cannotfindsco', 'scorm');
 886          }
 887  
 888          // Mark module viewed.
 889          $completion = new completion_info($course);
 890          $completion->set_module_viewed($cm);
 891  
 892          list($sco, $scolaunchurl) = scorm_get_sco_and_launch_url($scorm, $params['scoid'], $context);
 893          // Trigger the SCO launched event.
 894          scorm_launch_sco($scorm, $sco, $cm, $context, $scolaunchurl);
 895  
 896          $result = array();
 897          $result['status'] = true;
 898          $result['warnings'] = $warnings;
 899          return $result;
 900      }
 901  
 902      /**
 903       * Returns description of method result value
 904       *
 905       * @return external_description
 906       * @since Moodle 3.1
 907       */
 908      public static function launch_sco_returns() {
 909          return new external_single_structure(
 910              array(
 911                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
 912                  'warnings' => new external_warnings()
 913              )
 914          );
 915      }
 916  
 917      /**
 918       * Describes the parameters for get_scorm_access_information.
 919       *
 920       * @return external_external_function_parameters
 921       * @since Moodle 3.7
 922       */
 923      public static function get_scorm_access_information_parameters() {
 924          return new external_function_parameters (
 925              array(
 926                  'scormid' => new external_value(PARAM_INT, 'scorm instance id.')
 927              )
 928          );
 929      }
 930  
 931      /**
 932       * Return access information for a given scorm.
 933       *
 934       * @param int $scormid scorm instance id
 935       * @return array of warnings and the access information
 936       * @since Moodle 3.7
 937       * @throws  moodle_exception
 938       */
 939      public static function get_scorm_access_information($scormid) {
 940          global $DB;
 941  
 942          $params = self::validate_parameters(self::get_scorm_access_information_parameters(), array('scormid' => $scormid));
 943  
 944          // Request and permission validation.
 945          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 946          list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
 947  
 948          $context = context_module::instance($cm->id);
 949          self::validate_context($context);
 950  
 951          $result = array();
 952          // Return all the available capabilities.
 953          $capabilities = load_capability_def('mod_scorm');
 954          foreach ($capabilities as $capname => $capdata) {
 955              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 956              $field = 'can' . str_replace('mod/scorm:', '', $capname);
 957              $result[$field] = has_capability($capname, $context);
 958          }
 959  
 960          $result['warnings'] = array();
 961          return $result;
 962      }
 963  
 964      /**
 965       * Describes the get_scorm_access_information return value.
 966       *
 967       * @return external_single_structure
 968       * @since Moodle 3.7
 969       */
 970      public static function get_scorm_access_information_returns() {
 971  
 972          $structure = array(
 973              'warnings' => new external_warnings()
 974          );
 975  
 976          $capabilities = load_capability_def('mod_scorm');
 977          foreach ($capabilities as $capname => $capdata) {
 978              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 979              $field = 'can' . str_replace('mod/scorm:', '', $capname);
 980              $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.',
 981                  VALUE_OPTIONAL);
 982          }
 983  
 984          return new external_single_structure($structure);
 985      }
 986  }