From 02bff1ad824d016a6eb04e0eb51e7e9ec09738f7 Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Tue, 28 Jan 2014 14:15:31 +1300 Subject: [PATCH] MDL-43541 SCORM: set first launchable sco correctly. --- mod/scorm/datamodels/aicclib.php | 29 +++++++++++++-- mod/scorm/datamodels/scormlib.php | 59 +++++++++++++++++++++++++++---- mod/scorm/db/upgrade.php | 54 ++++++++++++++++++++++++++-- mod/scorm/locallib.php | 10 +++++- mod/scorm/version.php | 5 ++- 5 files changed, 140 insertions(+), 17 deletions(-) diff --git a/mod/scorm/datamodels/aicclib.php b/mod/scorm/datamodels/aicclib.php index 46754946775..da1ef1c2fac 100644 --- a/mod/scorm/datamodels/aicclib.php +++ b/mod/scorm/datamodels/aicclib.php @@ -14,6 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * functions used by AICC packages. + * + * @package mod_scorm + * @copyright 1999 onwards Roberto Pinna + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + function scorm_add_time($a, $b) { $aes = explode(':', $a); $bes = explode(':', $b); @@ -106,8 +114,14 @@ function scorm_forge_cols_regexp($columns, $remodule='(".*")?,') { return $regexp; } +/** + * Sets up AICC packages + * Called whenever package changes + * @param object $scorm instance - fields are updated and changes saved into database -function scorm_parse_aicc($scorm) { + * @return bool + */ +function scorm_parse_aicc(&$scorm) { global $DB; if ($scorm->scormtype == SCORM_TYPE_AICCURL) { @@ -359,9 +373,18 @@ function scorm_parse_aicc($scorm) { } } - $scorm->version = 'AICC'; + // Find first launchable object. + $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true); + // We use get_records here as we need to pass a limit in the query that works cross db. + $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1); + if (!empty($scoes)) { + $sco = reset($scoes); // We only care about the first record - the above query only returns one. + $scorm->launch = $sco->id; + } else { + $scorm->launch = $launch; + } - $scorm->launch = $launch; + $scorm->version = 'AICC'; return true; } diff --git a/mod/scorm/datamodels/scormlib.php b/mod/scorm/datamodels/scormlib.php index 91ef6d88b2e..b8b14e9ba28 100644 --- a/mod/scorm/datamodels/scormlib.php +++ b/mod/scorm/datamodels/scormlib.php @@ -14,6 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * functions used by SCORM 1.2/2004 packages. + * + * @package mod_scorm + * @copyright 1999 onwards Roberto Pinna + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + function scorm_get_resources($blocks) { $resources = array(); foreach ($blocks as $block) { @@ -503,7 +511,14 @@ function scorm_get_manifest($blocks, $scoes) { return $scoes; } -function scorm_parse_scorm($scorm, $manifest) { +/** + * Sets up SCORM 1.2/2004 packages using the manifest file. + * Called whenever SCORM changes + * @param object $scorm instance - fields are updated and changes saved into database + * @param stored_file|string $manifest - path to manifest file or stored_file. + * @return bool + */ +function scorm_parse_scorm(&$scorm, $manifest) { global $CFG, $DB; // load manifest into string @@ -514,7 +529,8 @@ function scorm_parse_scorm($scorm, $manifest) { $xmltext = download_file_content($manifest); } - $launch = 0; + $defaultorgid = 0; + $firstinorg = 0; $pattern = '/&(?!\w{2,6};)/'; $replacement = '&'; @@ -525,6 +541,7 @@ function scorm_parse_scorm($scorm, $manifest) { $scoes = new stdClass(); $scoes->version = ''; $scoes = scorm_get_manifest($manifests, $scoes); + $newscoes = array(); $sortorder = 0; if (count($scoes->elements) > 0) { $olditems = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id)); @@ -547,6 +564,10 @@ function scorm_parse_scorm($scorm, $manifest) { } } + if (!empty($defaultorgid) && empty($firstinorg) && $newitem->parent == $scoes->defaultorg) { + $firstinorg = $sortorder; + } + if (!empty($olditems) && ($olditemid = scorm_array_search('identifier', $newitem->identifier, $olditems))) { $newitem->id = $olditemid; // Update the Sco sortorder but keep id so that user tracks are kept against the same ids. @@ -567,6 +588,7 @@ function scorm_parse_scorm($scorm, $manifest) { // Insert the new SCO, and retain the link between the old and new for later adjustment $id = $DB->insert_record('scorm_scoes', $newitem); } + $newscoes[$id] = $newitem; // Save this sco in memory so we can use it later. if ($optionaldatas = scorm_optionals_data($item, $standarddatas)) { $data = new stdClass(); @@ -651,8 +673,8 @@ function scorm_parse_scorm($scorm, $manifest) { } } } - if (($launch == 0) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { - $launch = $id; + if (empty($defaultorgid) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { + $defaultorgid = $id; } } } @@ -673,11 +695,34 @@ function scorm_parse_scorm($scorm, $manifest) { if (empty($scoes->version)) { $scoes->version = 'SCORM_1.2'; } - $DB->set_field('scorm', 'version', $scoes->version, array('id'=>$scorm->id)); + $DB->set_field('scorm', 'version', $scoes->version, array('id' => $scorm->id)); $scorm->version = $scoes->version; } - - $scorm->launch = $launch; + $scorm->launch = 0; + // Check launch sco is valid. + if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && !empty($newscoes[$defaultorgid]->launch)) { + // Launch param is valid - do nothing. + $scorm->launch = $defaultorgid; + } else if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && empty($newscoes[$defaultorgid]->launch)) { + // The launch is probably the default org so we need to find the first launchable item inside this org. + $sqlselect = 'scorm = ? AND sortorder > ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true); + // We use get_records here as we need to pass a limit in the query that works cross db. + $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id, $firstinorg), 'sortorder', 'id', 0, 1); + if (!empty($scoes)) { + $sco = reset($scoes); // We only care about the first record - the above query only returns one. + $scorm->launch = $sco->id; + } + } + if (empty($scorm->launch)) { + // No valid Launch is specified - find the first launchable sco instead. + $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true); + // We use get_records here as we need to pass a limit in the query that works cross db. + $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1); + if (!empty($scoes)) { + $sco = reset($scoes); // We only care about the first record - the above query only returns one. + $scorm->launch = $sco->id; + } + } return true; } diff --git a/mod/scorm/db/upgrade.php b/mod/scorm/db/upgrade.php index 785f9bb1d6d..7275d42bd42 100644 --- a/mod/scorm/db/upgrade.php +++ b/mod/scorm/db/upgrade.php @@ -17,9 +17,8 @@ /** * Upgrade script for the scorm module. * - * @package mod - * @subpackage scorm - * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @package mod_scorm + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -203,6 +202,55 @@ function xmldb_scorm_upgrade($oldversion) { upgrade_mod_savepoint(true, 2013110501, 'scorm'); } + if ($oldversion < 2014021700) { + // Fix invalid $scorm->launch records that launch an org sco instead of a real sco. + $sql = "SELECT s.*, c.identifier + FROM {scorm} s + LEFT JOIN {scorm_scoes} c ON s.launch = c.id + WHERE ".$DB->sql_isempty('scorm_scoes', 'c.launch', false, true); + $scorms = $DB->get_recordset_sql($sql); + foreach ($scorms as $scorm) { + upgrade_set_timeout(60); // Increase execution time just in case. (60 sec is minimum but prob excessive here). + $originallaunch = $scorm->launch; + // Find the first sco using the current identifier as it's parent + // we use get records here as we need to pass a limit in the query that works cross db. + $firstsco = $DB->get_records('scorm_scoes', + array('scorm' => $scorm->id, 'parent' => $scorm->identifier), 'sortorder', '*', 0, 1); + if (!empty($firstsco)) { + $firstsco = reset($firstsco); + } + if (!empty($firstsco->launch)) { + // Usual behavior - this is a valid sco with a launch param so use it. + $scorm->launch = $firstsco->id; + } else { + // The firstsco found is not launchable - find the first launchable sco after this sco. + $sqlselect = 'scorm = ? AND sortorder > ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true); + // We use get_records here as we need to pass a limit in the query that works cross db. + $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, + array($scorm->id, $firstsco->sortorder), 'sortorder', 'id', 0, 1); + if (!empty($scoes)) { + $sco = reset($scoes); // We only care about the first record - the above query only returns one. + $scorm->launch = $sco->id; + } else { + // This is an invalid package - it has a default org that doesn't contain a launchable sco. + // Check for any valid sco with a launch param. + $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true); + // We use get_records here as we need to pass a limit in the query that works cross db. + $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1); + if (!empty($scoes)) { + $sco = reset($scoes); // We only care about the first record - the above query only returns one. + $scorm->launch = $sco->id; + } + } + } + if ($originallaunch != $scorm->launch) { + $DB->update_record('scorm', $scorm); + } + } + $scorms->close(); + + upgrade_mod_savepoint(true, 2014021700, 'scorm'); + } return true; } diff --git a/mod/scorm/locallib.php b/mod/scorm/locallib.php index eb77ac3410b..bd3bf470aec 100644 --- a/mod/scorm/locallib.php +++ b/mod/scorm/locallib.php @@ -14,6 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Library of internal classes and functions for module SCORM + * + * @package mod_scorm + * @copyright 1999 onwards Roberto Pinna + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + require_once("$CFG->dirroot/mod/scorm/lib.php"); require_once("$CFG->libdir/filelib.php"); @@ -897,7 +905,7 @@ function scorm_view_display ($user, $scorm, $action, $cm) { } ?>
- + diff --git a/mod/scorm/version.php b/mod/scorm/version.php index 5d370b0e6db..d384c6bf493 100644 --- a/mod/scorm/version.php +++ b/mod/scorm/version.php @@ -17,15 +17,14 @@ /** * scorm version information. * - * @package mod - * @subpackage scorm + * @package mod_scorm * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2013110501; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2014021700; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2013110500; // Requires this Moodle version $plugin->component = 'mod_scorm'; // Full name of the plugin (used for diagnostics) $plugin->cron = 300;