Search moodle.org's
Developer Documentation

See Release Notes

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

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [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  
 485          foreach ($params['tracks'] as $track) {
 486              $element = $track['element'];
 487              $value = $track['value'];
 488              $trackid = scorm_insert_track($USER->id, $scorm->id, $sco->id, $params['attempt'], $element, $value,
 489                                              $scorm->forcecompleted);
 490  
 491              if ($trackid) {
 492                  $trackids[] = $trackid;
 493              } else {
 494                  $warnings[] = array(
 495                      'item' => 'scorm',
 496                      'itemid' => $scorm->id,
 497                      'warningcode' => 1,
 498                      'message' => 'Element: ' . $element . ' was not saved'
 499                  );
 500              }
 501          }
 502  
 503          $result = array();
 504          $result['trackids'] = $trackids;
 505          $result['warnings'] = $warnings;
 506          return $result;
 507      }
 508  
 509      /**
 510       * Describes the insert_scorm_tracks return value.
 511       *
 512       * @return external_single_structure
 513       * @since Moodle 3.0
 514       */
 515      public static function insert_scorm_tracks_returns() {
 516  
 517          return new external_single_structure(
 518              array(
 519                  'trackids' => new external_multiple_structure(new external_value(PARAM_INT, 'track id')),
 520                  'warnings' => new external_warnings(),
 521              )
 522          );
 523      }
 524  
 525      /**
 526       * Describes the parameters for get_scorm_sco_tracks.
 527       *
 528       * @return external_function_parameters
 529       * @since Moodle 3.0
 530       */
 531      public static function get_scorm_sco_tracks_parameters() {
 532          return new external_function_parameters(
 533              array(
 534                  'scoid' => new external_value(PARAM_INT, 'sco id'),
 535                  'userid' => new external_value(PARAM_INT, 'user id'),
 536                  'attempt' => new external_value(PARAM_INT, 'attempt number (0 for last attempt)', VALUE_DEFAULT, 0)
 537              )
 538          );
 539      }
 540  
 541      /**
 542       * Retrieves SCO tracking data for the given user id and attempt number
 543       *
 544       * @param int $scoid the sco id
 545       * @param int $userid the user id
 546       * @param int $attempt the attempt number
 547       * @return array warnings and the scoes data
 548       * @since Moodle 3.0
 549       */
 550      public static function get_scorm_sco_tracks($scoid, $userid, $attempt = 0) {
 551          global $USER, $DB;
 552  
 553          $params = self::validate_parameters(self::get_scorm_sco_tracks_parameters(),
 554                                              array('scoid' => $scoid, 'userid' => $userid, 'attempt' => $attempt));
 555  
 556          $tracks = array();
 557          $warnings = array();
 558  
 559          $sco = scorm_get_sco($params['scoid'], SCO_ONLY);
 560          if (!$sco) {
 561              throw new moodle_exception('cannotfindsco', 'scorm');
 562          }
 563  
 564          $scorm = $DB->get_record('scorm', array('id' => $sco->scorm), '*', MUST_EXIST);
 565          $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 566  
 567          $context = context_module::instance($cm->id);
 568          self::validate_context($context);
 569  
 570          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 571          core_user::require_active_user($user);
 572  
 573          // Extra checks so only users with permissions can view other users attempts.
 574          if ($USER->id != $user->id) {
 575              require_capability('mod/scorm:viewreport', $context);
 576          }
 577  
 578          scorm_require_available($scorm, true, $context);
 579  
 580          if (empty($params['attempt'])) {
 581              $params['attempt'] = scorm_get_last_attempt($scorm->id, $user->id);
 582          }
 583  
 584          $attempted = false;
 585          if ($scormtracks = scorm_get_tracks($sco->id, $params['userid'], $params['attempt'])) {
 586              // Check if attempted.
 587              if ($scormtracks->status != '') {
 588                  $attempted = true;
 589                  foreach ($scormtracks as $element => $value) {
 590                      $tracks[] = array(
 591                          'element' => $element,
 592                          'value' => $value,
 593                      );
 594                  }
 595              }
 596          }
 597  
 598          if (!$attempted) {
 599              $warnings[] = array(
 600                  'item' => 'attempt',
 601                  'itemid' => $params['attempt'],
 602                  'warningcode' => 'notattempted',
 603                  'message' => get_string('notattempted', 'scorm')
 604              );
 605          }
 606  
 607          $result = array();
 608          $result['data']['attempt'] = $params['attempt'];
 609          $result['data']['tracks'] = $tracks;
 610          $result['warnings'] = $warnings;
 611          return $result;
 612      }
 613  
 614      /**
 615       * Describes the get_scorm_sco_tracks return value.
 616       *
 617       * @return external_single_structure
 618       * @since Moodle 3.0
 619       */
 620      public static function get_scorm_sco_tracks_returns() {
 621  
 622          return new external_single_structure(
 623              array(
 624                  'data' => new external_single_structure(
 625                      array(
 626                          'attempt' => new external_value(PARAM_INT, 'Attempt number'),
 627                          'tracks' => new external_multiple_structure(
 628                              new external_single_structure(
 629                                  array(
 630                                      'element' => new external_value(PARAM_RAW, 'Element name'),
 631                                      'value' => new external_value(PARAM_RAW, 'Element value')
 632                                  ), 'Tracks data'
 633                              )
 634                          ),
 635                      ), 'SCO data'
 636                  ),
 637                  'warnings' => new external_warnings(),
 638              )
 639          );
 640      }
 641  
 642      /**
 643       * Describes the parameters for get_scorms_by_courses.
 644       *
 645       * @return external_function_parameters
 646       * @since Moodle 3.0
 647       */
 648      public static function get_scorms_by_courses_parameters() {
 649          return new external_function_parameters (
 650              array(
 651                  'courseids' => new external_multiple_structure(
 652                      new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array()
 653                  ),
 654              )
 655          );
 656      }
 657  
 658      /**
 659       * Returns a list of scorms in a provided list of courses,
 660       * if no list is provided all scorms that the user can view will be returned.
 661       *
 662       * @param array $courseids the course ids
 663       * @return array the scorm details
 664       * @since Moodle 3.0
 665       */
 666      public static function get_scorms_by_courses($courseids = array()) {
 667          global $CFG;
 668  
 669          $returnedscorms = array();
 670          $warnings = array();
 671  
 672          $params = self::validate_parameters(self::get_scorms_by_courses_parameters(), array('courseids' => $courseids));
 673  
 674          $courses = array();
 675          if (empty($params['courseids'])) {
 676              $courses = enrol_get_my_courses();
 677              $params['courseids'] = array_keys($courses);
 678          }
 679  
 680          // Ensure there are courseids to loop through.
 681          if (!empty($params['courseids'])) {
 682  
 683              list($courses, $warnings) = util::validate_courses($params['courseids'], $courses);
 684  
 685              // Get the scorms in this course, this function checks users visibility permissions.
 686              // We can avoid then additional validate_context calls.
 687              $scorms = get_all_instances_in_courses("scorm", $courses);
 688  
 689              $fs = get_file_storage();
 690              foreach ($scorms as $scorm) {
 691  
 692                  $context = context_module::instance($scorm->coursemodule);
 693  
 694                  // Entry to return.
 695                  $module = helper_for_get_mods_by_courses::standard_coursemodule_element_values($scorm, 'mod_scorm');
 696  
 697                  // Check if the SCORM open and return warnings if so.
 698                  list($open, $openwarnings) = scorm_get_availability_status($scorm, true, $context);
 699  
 700                  if (!$open) {
 701                      foreach ($openwarnings as $warningkey => $warningdata) {
 702                          $warnings[] = array(
 703                              'item' => 'scorm',
 704                              'itemid' => $scorm->id,
 705                              'warningcode' => $warningkey,
 706                              'message' => get_string($warningkey, 'scorm', $warningdata)
 707                          );
 708                      }
 709                  } else {
 710                      $module['packagesize'] = 0;
 711                      // SCORM size.
 712                      if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) {
 713                          if ($packagefile = $fs->get_file($context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)) {
 714                              $module['packagesize'] = $packagefile->get_filesize();
 715                              // Download URL.
 716                              $module['packageurl'] = moodle_url::make_webservice_pluginfile_url(
 717                                                      $context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)->out(false);
 718                          }
 719                      }
 720  
 721                      $module['protectpackagedownloads'] = get_config('scorm', 'protectpackagedownloads');
 722  
 723                      $viewablefields = array('version', 'maxgrade', 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted',
 724                                              'forcenewattempt', 'lastattemptlock', 'displayattemptstatus', 'displaycoursestructure',
 725                                              'sha1hash', 'md5hash', 'revision', 'launch', 'skipview', 'hidebrowse', 'hidetoc', 'nav',
 726                                              'navpositionleft', 'navpositiontop', 'auto', 'popup', 'width', 'height', 'timeopen',
 727                                              'timeclose', 'scormtype', 'reference');
 728  
 729                      // Check additional permissions for returning optional private settings.
 730                      if (has_capability('moodle/course:manageactivities', $context)) {
 731                          $additionalfields = array('updatefreq', 'options', 'completionstatusrequired', 'completionscorerequired',
 732                                                    'completionstatusallscos', 'autocommit', 'timemodified');
 733                          $viewablefields = array_merge($viewablefields, $additionalfields);
 734                      }
 735  
 736                      foreach ($viewablefields as $field) {
 737                          $module[$field] = $scorm->{$field};
 738                      }
 739                  }
 740  
 741                  $returnedscorms[] = $module;
 742              }
 743          }
 744  
 745          $result = array();
 746          $result['scorms'] = $returnedscorms;
 747          $result['warnings'] = $warnings;
 748          return $result;
 749      }
 750  
 751      /**
 752       * Describes the get_scorms_by_courses return value.
 753       *
 754       * @return external_single_structure
 755       * @since Moodle 3.0
 756       */
 757      public static function get_scorms_by_courses_returns() {
 758  
 759          return new external_single_structure(
 760              array(
 761                  'scorms' => new external_multiple_structure(
 762                      new external_single_structure(array_merge(
 763                          helper_for_get_mods_by_courses::standard_coursemodule_elements_returns(),
 764                          [
 765                              'packagesize' => new external_value(PARAM_INT, 'SCORM zip package size', VALUE_OPTIONAL),
 766                              'packageurl' => new external_value(PARAM_URL, 'SCORM zip package URL', VALUE_OPTIONAL),
 767                              'version' => new external_value(PARAM_NOTAGS, 'SCORM version (SCORM_12, SCORM_13, SCORM_AICC)',
 768                                                              VALUE_OPTIONAL),
 769                              'maxgrade' => new external_value(PARAM_INT, 'Max grade', VALUE_OPTIONAL),
 770                              'grademethod' => new external_value(PARAM_INT, 'Grade method', VALUE_OPTIONAL),
 771                              'whatgrade' => new external_value(PARAM_INT, 'What grade', VALUE_OPTIONAL),
 772                              'maxattempt' => new external_value(PARAM_INT, 'Maximum number of attemtps', VALUE_OPTIONAL),
 773                              'forcecompleted' => new external_value(PARAM_BOOL, 'Status current attempt is forced to "completed"',
 774                                                                      VALUE_OPTIONAL),
 775                              'forcenewattempt' => new external_value(PARAM_INT, 'Controls re-entry behaviour',
 776                                                                      VALUE_OPTIONAL),
 777                              'lastattemptlock' => new external_value(PARAM_BOOL, 'Prevents to launch new attempts once finished',
 778                                                                      VALUE_OPTIONAL),
 779                              'displayattemptstatus' => new external_value(PARAM_INT, 'How to display attempt status',
 780                                                                              VALUE_OPTIONAL),
 781                              'displaycoursestructure' => new external_value(PARAM_BOOL, 'Display contents structure',
 782                                                                              VALUE_OPTIONAL),
 783                              'sha1hash' => new external_value(PARAM_NOTAGS, 'Package content or ext path hash', VALUE_OPTIONAL),
 784                              'md5hash' => new external_value(PARAM_NOTAGS, 'MD5 Hash of package file', VALUE_OPTIONAL),
 785                              'revision' => new external_value(PARAM_INT, 'Revison number', VALUE_OPTIONAL),
 786                              'launch' => new external_value(PARAM_INT, 'First content to launch', VALUE_OPTIONAL),
 787                              'skipview' => new external_value(PARAM_INT, 'How to skip the content structure page', VALUE_OPTIONAL),
 788                              'hidebrowse' => new external_value(PARAM_BOOL, 'Disable preview mode?', VALUE_OPTIONAL),
 789                              'hidetoc' => new external_value(PARAM_INT, 'How to display the SCORM structure in player',
 790                                                              VALUE_OPTIONAL),
 791                              'nav' => new external_value(PARAM_INT, 'Show navigation buttons', VALUE_OPTIONAL),
 792                              'navpositionleft' => new external_value(PARAM_INT, 'Navigation position left', VALUE_OPTIONAL),
 793                              'navpositiontop' => new external_value(PARAM_INT, 'Navigation position top', VALUE_OPTIONAL),
 794                              'auto' => new external_value(PARAM_BOOL, 'Auto continue?', VALUE_OPTIONAL),
 795                              'popup' => new external_value(PARAM_INT, 'Display in current or new window', VALUE_OPTIONAL),
 796                              'width' => new external_value(PARAM_INT, 'Frame width', VALUE_OPTIONAL),
 797                              'height' => new external_value(PARAM_INT, 'Frame height', VALUE_OPTIONAL),
 798                              'timeopen' => new external_value(PARAM_INT, 'Available from', VALUE_OPTIONAL),
 799                              'timeclose' => new external_value(PARAM_INT, 'Available to', VALUE_OPTIONAL),
 800                              'scormtype' => new external_value(PARAM_ALPHA, 'SCORM type', VALUE_OPTIONAL),
 801                              'reference' => new external_value(PARAM_NOTAGS, 'Reference to the package', VALUE_OPTIONAL),
 802                              'protectpackagedownloads' => new external_value(PARAM_BOOL, 'Protect package downloads?',
 803                                                                              VALUE_OPTIONAL),
 804                              'updatefreq' => new external_value(PARAM_INT, 'Auto-update frequency for remote packages',
 805                                                                  VALUE_OPTIONAL),
 806                              'options' => new external_value(PARAM_RAW, 'Additional options', VALUE_OPTIONAL),
 807                              'completionstatusrequired' => new external_value(PARAM_INT, 'Status passed/completed required?',
 808                                                                                  VALUE_OPTIONAL),
 809                              'completionscorerequired' => new external_value(PARAM_INT, 'Minimum score required', VALUE_OPTIONAL),
 810                              'completionstatusallscos' => new external_value(PARAM_INT, 'Require all scos to return completion status', VALUE_OPTIONAL),
 811                              'autocommit' => new external_value(PARAM_BOOL, 'Save track data automatically?', VALUE_OPTIONAL),
 812                              'timemodified' => new external_value(PARAM_INT, 'Time of last modification', VALUE_OPTIONAL),
 813                          ]
 814                      ), 'SCORM')
 815                  ),
 816                  'warnings' => new external_warnings(),
 817              )
 818          );
 819      }
 820  
 821      /**
 822       * Returns description of method parameters
 823       *
 824       * @return external_function_parameters
 825       * @since Moodle 3.1
 826       */
 827      public static function launch_sco_parameters() {
 828          return new external_function_parameters(
 829              array(
 830                  'scormid' => new external_value(PARAM_INT, 'SCORM instance id'),
 831                  'scoid' => new external_value(PARAM_INT, 'SCO id (empty for launching the first SCO)', VALUE_DEFAULT, 0)
 832              )
 833          );
 834      }
 835  
 836      /**
 837       * Trigger the course module viewed event.
 838       *
 839       * @param int $scormid the SCORM instance id
 840       * @param int $scoid the SCO id
 841       * @return array of warnings and status result
 842       * @since Moodle 3.1
 843       * @throws moodle_exception
 844       */
 845      public static function launch_sco($scormid, $scoid = 0) {
 846          global $DB, $CFG;
 847  
 848          require_once($CFG->libdir . '/completionlib.php');
 849  
 850          $params = self::validate_parameters(self::launch_sco_parameters(),
 851                                              array(
 852                                                  'scormid' => $scormid,
 853                                                  'scoid' => $scoid
 854                                              ));
 855          $warnings = array();
 856  
 857          // Request and permission validation.
 858          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 859          list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
 860  
 861          $context = context_module::instance($cm->id);
 862          self::validate_context($context);
 863  
 864          // If the SCORM is not open this function will throw exceptions.
 865          scorm_require_available($scorm);
 866  
 867          if (!empty($params['scoid']) and !($sco = scorm_get_sco($params['scoid'], SCO_ONLY))) {
 868              throw new moodle_exception('cannotfindsco', 'scorm');
 869          }
 870  
 871          // Mark module viewed.
 872          $completion = new completion_info($course);
 873          $completion->set_module_viewed($cm);
 874  
 875          list($sco, $scolaunchurl) = scorm_get_sco_and_launch_url($scorm, $params['scoid'], $context);
 876          // Trigger the SCO launched event.
 877          scorm_launch_sco($scorm, $sco, $cm, $context, $scolaunchurl);
 878  
 879          $result = array();
 880          $result['status'] = true;
 881          $result['warnings'] = $warnings;
 882          return $result;
 883      }
 884  
 885      /**
 886       * Returns description of method result value
 887       *
 888       * @return \core_external\external_description
 889       * @since Moodle 3.1
 890       */
 891      public static function launch_sco_returns() {
 892          return new external_single_structure(
 893              array(
 894                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
 895                  'warnings' => new external_warnings()
 896              )
 897          );
 898      }
 899  
 900      /**
 901       * Describes the parameters for get_scorm_access_information.
 902       *
 903       * @return external_external_function_parameters
 904       * @since Moodle 3.7
 905       */
 906      public static function get_scorm_access_information_parameters() {
 907          return new external_function_parameters (
 908              array(
 909                  'scormid' => new external_value(PARAM_INT, 'scorm instance id.')
 910              )
 911          );
 912      }
 913  
 914      /**
 915       * Return access information for a given scorm.
 916       *
 917       * @param int $scormid scorm instance id
 918       * @return array of warnings and the access information
 919       * @since Moodle 3.7
 920       * @throws  moodle_exception
 921       */
 922      public static function get_scorm_access_information($scormid) {
 923          global $DB;
 924  
 925          $params = self::validate_parameters(self::get_scorm_access_information_parameters(), array('scormid' => $scormid));
 926  
 927          // Request and permission validation.
 928          $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
 929          list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
 930  
 931          $context = context_module::instance($cm->id);
 932          self::validate_context($context);
 933  
 934          $result = array();
 935          // Return all the available capabilities.
 936          $capabilities = load_capability_def('mod_scorm');
 937          foreach ($capabilities as $capname => $capdata) {
 938              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 939              $field = 'can' . str_replace('mod/scorm:', '', $capname);
 940              $result[$field] = has_capability($capname, $context);
 941          }
 942  
 943          $result['warnings'] = array();
 944          return $result;
 945      }
 946  
 947      /**
 948       * Describes the get_scorm_access_information return value.
 949       *
 950       * @return external_single_structure
 951       * @since Moodle 3.7
 952       */
 953      public static function get_scorm_access_information_returns() {
 954  
 955          $structure = array(
 956              'warnings' => new external_warnings()
 957          );
 958  
 959          $capabilities = load_capability_def('mod_scorm');
 960          foreach ($capabilities as $capname => $capdata) {
 961              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 962              $field = 'can' . str_replace('mod/scorm:', '', $capname);
 963              $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.',
 964                  VALUE_OPTIONAL);
 965          }
 966  
 967          return new external_single_structure($structure);
 968      }
 969  }