Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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