diff --git a/lang/en_utf8/scorm.php b/lang/en_utf8/scorm.php index 06c7149cfe4..8cf989ed517 100644 --- a/lang/en_utf8/scorm.php +++ b/lang/en_utf8/scorm.php @@ -4,6 +4,12 @@ $string['activation'] = 'Activation'; $string['advanced'] = 'Parameters'; +$string['allowtypeexternal'] = 'Enable external package type'; +$string['allowtypeimsrepository'] = 'Enable IMS package type'; +$string['allowtypelocalsync'] = 'Enable downloaded package type'; +$string['areaintro'] = 'Introduction files'; +$string['areacontent'] = 'Content files'; +$string['areapackage'] = 'Package file'; $string['asset'] = 'Asset'; $string['assetlaunched'] = 'Asset - Viewed'; $string['attempt'] = 'attempt'; @@ -125,6 +131,7 @@ $string['scorm:skipview'] = 'Skip overview'; $string['scorm:viewreport'] = 'View reports'; $string['scorm:viewscores'] = 'View scores'; $string['scormcourse'] = 'Learning Course'; +$string['scormtype'] = 'Type'; $string['scrollbars'] = 'Allow the window to be scrolled'; $string['sided'] = 'On the left side'; $string['skipview'] = 'Student skip content structure page'; @@ -145,6 +152,12 @@ $string['toolbar'] = 'Show the toolbar'; $string['totaltime'] = 'Time'; $string['trackingloose'] = 'WARNING: The tracking data of this package will be lost!'; $string['type'] = 'Type'; +$string['typeexternal'] = 'External SCORM manifest'; +$string['typeimsrepository'] = 'Local IMS content repository'; +$string['typelocal'] = 'Uploaded package'; +$string['typelocalsync'] = 'Downloaded package'; +$string['updatetime'] = 'Synchronisation time'; +$string['url'] = 'URL'; $string['validateascorm'] = 'Validate a package'; $string['validation'] = 'Validation result'; $string['validationtype'] = 'This preference set the DOMXML library used for validating SCORM Manifest. If you don\'t know leave the selected choice.'; diff --git a/mod/scorm/api.php b/mod/scorm/api.php index 771cd4b1942..ca6613a9d8c 100644 --- a/mod/scorm/api.php +++ b/mod/scorm/api.php @@ -2,7 +2,7 @@ require_once("../../config.php"); require_once('locallib.php'); - + $id = optional_param('id', '', PARAM_INT); // Course Module ID, or $a = optional_param('a', '', PARAM_INT); // scorm ID $scoid = required_param('scoid', PARAM_INT); // sco ID @@ -33,7 +33,7 @@ print_error('missingparameter'); } - require_login($course->id, false, $cm); + require_login($course, false, $cm); if ($usertrack = scorm_get_tracks($scoid,$USER->id,$attempt)) { if ((isset($usertrack->{'cmi.exit'}) && ($usertrack->{'cmi.exit'} != 'time-out')) || ($scorm->version != "SCORM_1.3")) { @@ -58,7 +58,7 @@ $userdata->credit = 'credit'; } else { $userdata->credit = 'no-credit'; - } + } if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) { foreach ($scodatas as $key => $value) { $userdata->$key = addslashes_js($value); diff --git a/mod/scorm/datamodels/aicclib.php b/mod/scorm/datamodels/aicclib.php index 908e7e2d5ee..7adf7fc18c1 100644 --- a/mod/scorm/datamodels/aicclib.php +++ b/mod/scorm/datamodels/aicclib.php @@ -90,49 +90,57 @@ function scorm_forge_cols_regexp($columns,$remodule='(".*")?,') { return $regexp; } -function scorm_parse_aicc($pkgdir,$scormid) { + +function scorm_parse_aicc($scorm) { global $DB; + if (!isset($scorm->cmid)) { + $cm = get_coursemodule_from_instance('scorm', $scorm->id); + $scorm->cmid = $cm->id; + } + $context = get_context_instance(COURSE_MODULE, $scorm->cmid); + + $fs = get_file_storage(); + + $files = $fs->get_area_files($context->id, 'scorm_content', 0, '', false); + + $version = 'AICC'; $ids = array(); $courses = array(); $extaiccfiles = array('crs','des','au','cst','ort','pre','cmp'); - if ($handle = opendir($pkgdir)) { - while (($file = readdir($handle)) !== false) { - if ($file[0] != '.') { - $ext = substr($file,strrpos($file,'.')); - $extension = strtolower(substr($ext,1)); - if (in_array($extension,$extaiccfiles)) { - $id = strtolower(basename($file,$ext)); - $ids[$id]->$extension = $file; - } - } + + foreach ($files as $file) { + $filename = $file->get_filename(); + $ext = substr($filename,strrpos($filename,'.')); + $extension = strtolower(substr($ext,1)); + if (in_array($extension,$extaiccfiles)) { + $id = strtolower(basename($filename,$ext)); + $ids[$id]->$extension = $file; } - closedir($handle); } + foreach ($ids as $courseid => $id) { if (isset($id->crs)) { - if (is_file($pkgdir.'/'.$id->crs)) { - $rows = file($pkgdir.'/'.$id->crs); - foreach ($rows as $row) { - if (preg_match("/^(.+)=(.+)$/",$row,$matches)) { - switch (strtolower(trim($matches[1]))) { - case 'course_id': - $courses[$courseid]->id = trim($matches[2]); - break; - case 'course_title': - $courses[$courseid]->title = trim($matches[2]); - break; - case 'version': - $courses[$courseid]->version = 'AICC_'.trim($matches[2]); - break; - } + $rows = $id->crs->get_content(); + foreach ($rows as $row) { + if (preg_match("/^(.+)=(.+)$/",$row,$matches)) { + switch (strtolower(trim($matches[1]))) { + case 'course_id': + $courses[$courseid]->id = trim($matches[2]); + break; + case 'course_title': + $courses[$courseid]->title = trim($matches[2]); + break; + case 'version': + $courses[$courseid]->version = 'AICC_'.trim($matches[2]); + break; } } } } if (isset($id->des)) { - $rows = file($pkgdir.'/'.$id->des); + $rows = $id->des->get_content(); $columns = scorm_get_aicc_columns($rows[0]); $regexp = scorm_forge_cols_regexp($columns->columns); for ($i=1;$iau)) { - $rows = file($pkgdir.'/'.$id->au); + $rows = $id->au->get_content(); $columns = scorm_get_aicc_columns($rows[0]); $regexp = scorm_forge_cols_regexp($columns->columns); for ($i=1;$icst)) { - $rows = file($pkgdir.'/'.$id->cst); + $rows = $id->cst->get_content(); $columns = scorm_get_aicc_columns($rows[0],'block'); $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,'); for ($i=1;$iort)) { - $rows = file($pkgdir.'/'.$id->ort); + $rows = $id->ort->get_content(); $columns = scorm_get_aicc_columns($rows[0],'course_element'); $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,'); for ($i=1;$ipre)) { - $rows = file($pkgdir.'/'.$id->pre); + $rows = $id->pre->get_content(); $columns = scorm_get_aicc_columns($rows[0],'structure_element'); $regexp = scorm_forge_cols_regexp($columns->columns,'(.+),'); for ($i=1;$icmp)) { - $rows = file($pkgdir.'/'.$id->cmp); + $rows = $id->cmp->get_content(); } } //print_r($courses); - $oldscoes = $DB->get_records('scorm_scoes', array('scorm'=>$scormid)); + $oldscoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id)); $launch = 0; if (isset($courses)) { foreach ($courses as $course) { $sco = new object(); $sco->identifier = $course->id; - $sco->scorm = $scormid; + $sco->scorm = $scorm->id; $sco->organization = ''; $sco->title = $course->title; $sco->parent = '/'; @@ -216,7 +224,7 @@ function scorm_parse_aicc($pkgdir,$scormid) { $sco->scormtype = ''; //print_r($sco); - if ($ss = $DB->get_record('scorm_scoes', array('scorm'=>$scormid,'identifier'=>$sco->identifier))) { + if ($ss = $DB->get_record('scorm_scoes', array('scorm'=>$scorm->id,'identifier'=>$sco->identifier))) { $id = $ss->id; $DB->update_record('scorm_scoes',$sco); unset($oldscoes[$id]); @@ -231,7 +239,7 @@ function scorm_parse_aicc($pkgdir,$scormid) { foreach($course->elements as $element) { unset($sco); $sco->identifier = $element->system_id; - $sco->scorm = $scormid; + $sco->scorm = $scorm->id; $sco->organization = $course->id; $sco->title = $element->title; @@ -302,8 +310,12 @@ function scorm_parse_aicc($pkgdir,$scormid) { $DB->delete_records('scorm_scoes_track', array('scoid'=>$oldsco->id)); } } - $DB->set_field('scorm','version','AICC', array('id'=>$scormid)); - return $launch; + + $scorm->version = 'AICC'; + + $scorm->launch = $launch; + + return true; } function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) { diff --git a/mod/scorm/datamodels/scormlib.php b/mod/scorm/datamodels/scormlib.php index 68e49d7b4ed..aca4b745b08 100644 --- a/mod/scorm/datamodels/scormlib.php +++ b/mod/scorm/datamodels/scormlib.php @@ -456,161 +456,165 @@ function scorm_get_manifest($blocks,$scoes) { return $scoes; } -function scorm_parse_scorm($pkgdir,$scormid) { +function scorm_parse_scorm($scorm, $manifest) { global $CFG, $DB; + + // load manifest into string + if ($manifest instanceof stored_file) { + $xmltext = $manifest->get_content(); + } else { + require_once "$CFG->libdir/filelib.php"; + $xmltext = download_file_content($manifest); + } $launch = 0; - $manifestfile = $pkgdir.'/imsmanifest.xml'; - if (is_file($manifestfile)) { - - $xmltext = file_get_contents($manifestfile); + $pattern = '/&(?!\w{2,6};)/'; + $replacement = '&'; + $xmltext = preg_replace($pattern, $replacement, $xmltext); - $pattern = '/&(?!\w{2,6};)/'; - $replacement = '&'; - $xmltext = preg_replace($pattern, $replacement, $xmltext); - - $objXML = new xml2Array(); - $manifests = $objXML->parse($xmltext); + $objXML = new xml2Array(); + $manifests = $objXML->parse($xmltext); //print_object($manifests); - $scoes = new stdClass(); - $scoes->version = ''; - $scoes = scorm_get_manifest($manifests,$scoes); + $scoes = new stdClass(); + $scoes->version = ''; + $scoes = scorm_get_manifest($manifests,$scoes); //print_object($scoes); - if (count($scoes->elements) > 0) { - $olditems = $DB->get_records('scorm_scoes', array('scorm'=>$scormid)); - foreach ($scoes->elements as $manifest => $organizations) { - foreach ($organizations as $organization => $items) { - foreach ($items as $identifier => $item) { - // This new db mngt will support all SCORM future extensions - $newitem = new stdClass(); - $newitem->scorm = $scormid; - $newitem->manifest = $manifest; - $newitem->organization = $organization; - $standarddatas = array('parent', 'identifier', 'launch', 'scormtype', 'title'); - foreach ($standarddatas as $standarddata) { - if (isset($item->$standarddata)) { - $newitem->$standarddata = $item->$standarddata; + if (count($scoes->elements) > 0) { + $olditems = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id)); + foreach ($scoes->elements as $manifest => $organizations) { + foreach ($organizations as $organization => $items) { + foreach ($items as $identifier => $item) { + // This new db mngt will support all SCORM future extensions + $newitem = new stdClass(); + $newitem->scorm = $scorm->id; + $newitem->manifest = $manifest; + $newitem->organization = $organization; + $standarddatas = array('parent', 'identifier', 'launch', 'scormtype', 'title'); + foreach ($standarddatas as $standarddata) { + if (isset($item->$standarddata)) { + $newitem->$standarddata = $item->$standarddata; + } + } + + // Insert the new SCO, and retain the link between the old and new for later adjustment + $id = $DB->insert_record('scorm_scoes',$newitem); + if (!empty($olditems) && ($olditemid = scorm_array_search('identifier',$newitem->identifier,$olditems))) { + $olditems[$olditemid]->newid = $id; + } + + if ($optionaldatas = scorm_optionals_data($item,$standarddatas)) { + $data = new stdClass(); + $data->scoid = $id; + foreach ($optionaldatas as $optionaldata) { + if (isset($item->$optionaldata)) { + $data->name = $optionaldata; + $data->value = $item->$optionaldata; + $dataid = $DB->insert_record('scorm_scoes_data',$data); } } - - // Insert the new SCO, and retain the link between the old and new for later adjustment - $id = $DB->insert_record('scorm_scoes',$newitem); - if (!empty($olditems) && ($olditemid = scorm_array_search('identifier',$newitem->identifier,$olditems))) { - $olditems[$olditemid]->newid = $id; - } - - if ($optionaldatas = scorm_optionals_data($item,$standarddatas)) { - $data = new stdClass(); - $data->scoid = $id; - foreach ($optionaldatas as $optionaldata) { - if (isset($item->$optionaldata)) { - $data->name = $optionaldata; - $data->value = $item->$optionaldata; - $dataid = $DB->insert_record('scorm_scoes_data',$data); + } + + if (isset($item->sequencingrules)) { + foreach($item->sequencingrules as $sequencingrule) { + $rule = new stdClass(); + $rule->scoid = $id; + $rule->ruletype = $sequencingrule->type; + $rule->conditioncombination = $sequencingrule->conditioncombination; + $rule->action = $sequencingrule->action; + $ruleid = $DB->insert_record('scorm_seq_ruleconds',$rule); + if (isset($sequencingrule->ruleconditions)) { + foreach($sequencingrule->ruleconditions as $rulecondition) { + $rulecond = new stdClass(); + $rulecond->scoid = $id; + $rulecond->ruleconditionsid = $ruleid; + $rulecond->referencedobjective = $rulecondition->referencedobjective; + $rulecond->measurethreshold = $rulecondition->measurethreshold; + $rulecond->cond = $rulecondition->cond; + $rulecondid = $DB->insert_record('scorm_seq_rulecond',$rulecond); } } - } + } + } + + if (isset($item->rolluprules)) { + foreach($item->rolluprules as $rolluprule) { + $rollup = new stdClass(); + $rollup->scoid = $id; + $rollup->childactivityset = $rolluprule->childactivityset; + $rollup->minimumcount = $rolluprule->minimumcount; + $rollup->minimumpercent = $rolluprule->minimumpercent; + $rollup->rollupruleaction = $rolluprule->rollupruleaction; + $rollup->conditioncombination = $rolluprule->conditioncombination; - if (isset($item->sequencingrules)) { - foreach($item->sequencingrules as $sequencingrule) { - $rule = new stdClass(); - $rule->scoid = $id; - $rule->ruletype = $sequencingrule->type; - $rule->conditioncombination = $sequencingrule->conditioncombination; - $rule->action = $sequencingrule->action; - $ruleid = $DB->insert_record('scorm_seq_ruleconds',$rule); - if (isset($sequencingrule->ruleconditions)) { - foreach($sequencingrule->ruleconditions as $rulecondition) { - $rulecond = new stdClass(); - $rulecond->scoid = $id; - $rulecond->ruleconditionsid = $ruleid; - $rulecond->referencedobjective = $rulecondition->referencedobjective; - $rulecond->measurethreshold = $rulecondition->measurethreshold; - $rulecond->cond = $rulecondition->cond; - $rulecondid = $DB->insert_record('scorm_seq_rulecond',$rulecond); - } + $rollupruleid = $DB->insert_record('scorm_seq_rolluprule',$rollup); + if (isset($rollup->conditions)) { + foreach($rollup->conditions as $condition){ + $cond = new stdClass(); + $cond->scoid = $rollup->scoid; + $cond->rollupruleid = $rollupruleid; + $cond->operator = $condition->operator; + $cond->cond = $condition->cond; + $conditionid = $DB->insert_record('scorm_seq_rolluprulecond',$cond); } - } - } - - if (isset($item->rolluprules)) { - foreach($item->rolluprules as $rolluprule) { - $rollup = new stdClass(); - $rollup->scoid = $id; - $rollup->childactivityset = $rolluprule->childactivityset; - $rollup->minimumcount = $rolluprule->minimumcount; - $rollup->minimumpercent = $rolluprule->minimumpercent; - $rollup->rollupruleaction = $rolluprule->rollupruleaction; - $rollup->conditioncombination = $rolluprule->conditioncombination; + } + } + } - $rollupruleid = $DB->insert_record('scorm_seq_rolluprule',$rollup); - if (isset($rollup->conditions)) { - foreach($rollup->conditions as $condition){ - $cond = new stdClass(); - $cond->scoid = $rollup->scoid; - $cond->rollupruleid = $rollupruleid; - $cond->operator = $condition->operator; - $cond->cond = $condition->cond; - $conditionid = $DB->insert_record('scorm_seq_rolluprulecond',$cond); - } - } - } - } - - if (isset($item->objectives)) { - foreach($item->objectives as $objective) { - $obj = new stdClass(); - $obj->scoid = $id; - $obj->primaryobj = $objective->primaryobj; - $obj->satisfiedbumeasure = $objective->satisfiedbymeasure; - $obj->objectiveid = $objective->objectiveid; - $obj->minnormalizedmeasure = $objective->minnormalizedmeasure; - $objectiveid = $DB->insert_record('scorm_seq_objective',$obj); - if (isset($objective->mapinfos)) { + if (isset($item->objectives)) { + foreach($item->objectives as $objective) { + $obj = new stdClass(); + $obj->scoid = $id; + $obj->primaryobj = $objective->primaryobj; + $obj->satisfiedbumeasure = $objective->satisfiedbymeasure; + $obj->objectiveid = $objective->objectiveid; + $obj->minnormalizedmeasure = $objective->minnormalizedmeasure; + $objectiveid = $DB->insert_record('scorm_seq_objective',$obj); + if (isset($objective->mapinfos)) { //print_object($objective->mapinfos); - foreach($objective->mapinfos as $objmapinfo) { - $mapinfo = new stdClass(); - $mapinfo->scoid = $id; - $mapinfo->objectiveid = $objectiveid; - $mapinfo->targetobjectiveid = $objmapinfo->targetobjectiveid; - $mapinfo->readsatisfiedstatus = $objmapinfo->readsatisfiedstatus; - $mapinfo->writesatisfiedstatus = $objmapinfo->writesatisfiedstatus; - $mapinfo->readnormalizedmeasure = $objmapinfo->readnormalizedmeasure; - $mapinfo->writenormalizedmeasure = $objmapinfo->writenormalizedmeasure; - $mapinfoid = $DB->insert_record('scorm_seq_mapinfo',$mapinfo); - } + foreach($objective->mapinfos as $objmapinfo) { + $mapinfo = new stdClass(); + $mapinfo->scoid = $id; + $mapinfo->objectiveid = $objectiveid; + $mapinfo->targetobjectiveid = $objmapinfo->targetobjectiveid; + $mapinfo->readsatisfiedstatus = $objmapinfo->readsatisfiedstatus; + $mapinfo->writesatisfiedstatus = $objmapinfo->writesatisfiedstatus; + $mapinfo->readnormalizedmeasure = $objmapinfo->readnormalizedmeasure; + $mapinfo->writenormalizedmeasure = $objmapinfo->writenormalizedmeasure; + $mapinfoid = $DB->insert_record('scorm_seq_mapinfo',$mapinfo); } } } + } //print_object($item); - if (($launch == 0) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { - $launch = $id; - } + if (($launch == 0) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { + $launch = $id; } } } - if (!empty($olditems)) { - foreach($olditems as $olditem) { - $DB->delete_records('scorm_scoes', array('id'=>$olditem->id)); - $DB->delete_records('scorm_scoes_data',array('scoid'=>$olditem->id)); - if (isset($olditem->newid)) { - $DB->set_field('scorm_scoes_track', 'scoid', $olditem->newid, array('scoid' => $olditem->id)); - } - $DB->delete_records('scorm_scoes_track',array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_objective', array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_mapinfo', array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_ruleconds', array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_rulecond', array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_rolluprule', array('scoid'=>$olditem->id)); - $DB->delete_records('scorm_seq_rolluprulecond', array('scoid'=>$olditem->id)); - } - } - $DB->set_field('scorm','version',$scoes->version, array('id'=>$scormid)); } - } + if (!empty($olditems)) { + foreach($olditems as $olditem) { + $DB->delete_records('scorm_scoes', array('id'=>$olditem->id)); + $DB->delete_records('scorm_scoes_data',array('scoid'=>$olditem->id)); + if (isset($olditem->newid)) { + $DB->set_field('scorm_scoes_track', 'scoid', $olditem->newid, array('scoid' => $olditem->id)); + } + $DB->delete_records('scorm_scoes_track',array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_objective', array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_mapinfo', array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_ruleconds', array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_rulecond', array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_rolluprule', array('scoid'=>$olditem->id)); + $DB->delete_records('scorm_seq_rolluprulecond', array('scoid'=>$olditem->id)); + } + } + $scorm->version = $scoes->version; + } - return $launch; + $scorm->launch = $launch; + + return true; } function scorm_optionals_data($item, $standarddata) { diff --git a/mod/scorm/db/install.xml b/mod/scorm/db/install.xml index 1386d83135d..12642de474d 100644 --- a/mod/scorm/db/install.xml +++ b/mod/scorm/db/install.xml @@ -1,5 +1,5 @@ - @@ -8,17 +8,20 @@ - - + + + - - - + + + + + @@ -31,7 +34,7 @@ - + @@ -194,4 +197,4 @@ - + \ No newline at end of file diff --git a/mod/scorm/db/upgrade.php b/mod/scorm/db/upgrade.php index 1a0c0ab9b09..9061b248e9c 100644 --- a/mod/scorm/db/upgrade.php +++ b/mod/scorm/db/upgrade.php @@ -1,6 +1,6 @@ set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'grademethod'); - + /// Launch add field whatgrade if (!$dbman->field_exists($table,$field)) { $dbman->add_field($table, $field); } - + upgrade_mod_savepoint($result, 2008073000, 'scorm'); } - + + if ($result && $oldversion < 2008082500) { + + /// Define field scormtype to be added to scorm + $table = new xmldb_table('scorm'); + $field = new xmldb_field('scormtype', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, null, null, 'local', 'name'); + + /// Launch add field scormtype + $dbman->add_field($table, $field); + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008082500, 'scorm'); + } + + if ($result && $oldversion < 2008090300) { + + /// Define field sha1hash to be added to scorm + $table = new xmldb_table('scorm'); + $field = new xmldb_field('sha1hash', XMLDB_TYPE_CHAR, '40', null, null, null, null, null, null, 'updatefreq'); + + /// Launch add field sha1hash + $dbman->add_field($table, $field); + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008090300, 'scorm'); + } + + if ($result && $oldversion < 2008090301) { + + /// Define field revision to be added to scorm + $table = new xmldb_table('scorm'); + $field = new xmldb_field('revision', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'md5hash'); + + /// Launch add field revision + $dbman->add_field($table, $field); + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008090301, 'scorm'); + } + + if ($result && $oldversion < 2008090302) { + $sql = "UPDATE {scorm} + SET scormtype = 'external' + WHERE reference LIKE ? OR reference LIKE ? OR reference LIKE ?"; + $DB->execute($sql, array('http://%imsmanifest.xml', 'https://%imsmanifest.xml', 'www.%imsmanifest.xml')); + + $sql = "UPDATE {scorm} + SET scormtype = 'localsync' + WHERE reference LIKE ? OR reference LIKE ? OR reference LIKE ? + OR reference LIKE ? OR reference LIKE ? OR reference LIKE ?"; + $DB->execute($sql, array('http://%.zip', 'https://%.zip', 'www.%.zip', 'http://%.pif', 'https://%.pif', 'www.%.pif')); + + $sql = "UPDATE {scorm} SET scormtype = 'imsrepository' WHERE reference LIKE ?"; + $DB->execute($sql, array('#%')); + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008090302, 'scorm'); + } + + if ($result && $oldversion < 2008090303) { + //remove obsoleted config settings + unset_config('scorm_advancedsettings'); + unset_config('scorm_windowsettings'); + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008090303, 'scorm'); + } + + if ($result && $oldversion < 2008090304) { + + ///////////////////////////////////// + /// new file storage upgrade code /// + ///////////////////////////////////// + + function scorm_migrate_content_files($context, $base, $path) { + global $CFG; + + $fullpathname = $base.$path; + $fs = get_file_storage(); + $filearea = 'scorm_content'; + $items = new DirectoryIterator($fullpathname); + + foreach ($items as $item) { + if ($item->isDot()) { + unset($item); // release file handle + continue; + } + + if ($item->isLink()) { + // do not follow symlinks - they were never supported in moddata, sorry + unset($item); // release file handle + continue; + } + + if ($item->isFile()) { + if (!$item->isReadable()) { + notify(" File not readable, skipping: ".$fullpathname.$item->getFilename()); + unset($item); // release file handle + continue; + } + + $filepath = clean_param($path, PARAM_PATH); + $filename = clean_param($item->getFilename(), PARAM_FILE); + $oldpathname = $fullpathname.$item->getFilename(); + + if ($filename === '') { + continue; + unset($item); // release file handle + } + + if (!$fs->file_exists($context->id, $filearea, '0', $filepath, $filename)) { + $file_record = array('contextid'=>$context->id, 'filearea'=>$filearea, 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename, + 'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime()); + unset($item); // release file handle + if ($fs->create_file_from_pathname($file_record, $oldpathname)) { + @unlink($oldpathname); + } + } else { + unset($item); // release file handle + } + + } else { + //migrate recursively all subdirectories + $oldpathname = $fullpathname.$item->getFilename().'/'; + $subpath = $path.$item->getFilename().'/'; + unset($item); // release file handle + scorm_migrate_content_files($context, $base, $subpath); + @rmdir($oldpathname); // deletes dir if empty + } + } + unset($items); //release file handles + } + + $fs = get_file_storage(); + + $sqlfrom = "FROM {scorm} s + JOIN {modules} m ON m.name = 'scorm' + JOIN {course_modules} cm ON (cm.module = m.id AND cm.instance = s.id)"; + + $count = $DB->count_records_sql("SELECT COUNT('x') $sqlfrom"); + + if ($rs = $DB->get_recordset_sql("SELECT s.id, s.scormtype, s.reference, s.course, cm.id AS cmid $sqlfrom ORDER BY s.course, s.id")) { + + $pbar = new progress_bar('migratescormfiles', 500, true); + + $olddebug = $DB->get_debug(); + $DB->set_debug(false); // lower debug level, there might be many files + $i = 0; + foreach ($rs as $scorm) { + $i++; + upgrade_set_timeout(180); // set up timeout, may also abort execution + $pbar->update($i, $count, "Migrating scorm files - $i/$count."); + + $context = get_context_instance(CONTEXT_MODULE, $scorm->cmid); + $coursecontext = get_context_instance(CONTEXT_COURSE, $scorm->course); + + // first copy local packages if found - do not delete in case they are shared ;-) + if ($scorm->scormtype === 'local' and preg_match('/.*(\.zip|\.pif)$/i', $scorm->reference)) { + $packagefile = '/'.clean_param($scorm->reference, PARAM_PATH); + $pathnamehash = sha1($coursecontext->id.'course_content0'.$packagefile); + if ($file = $fs->get_file_by_hash($pathnamehash)) { + $file_record = array('scontextid'=>$context->id, 'filearea'=>'scorm_pacakge', + 'itemid'=>0, 'filepath'=>'/'); + $packagefile = $fs->create_file_from_storedfile($file_record, $file); + $scorm->reference = $packagefile->get_filename(); + } else { + $scorm->reference = ''; + } + $DB->update_record('scorm', $scorm); + } + + // now migrate the extracted package + $basepath = "$CFG->dataroot/$scorm->course/$CFG->moddata/scorm/$scorm->id"; + if (!is_dir($basepath)) { + //no files? + continue; + } + + scorm_migrate_content_files($context, $basepath, '/'); + + // remove dirs if empty + @rmdir("$CFG->dataroot/$scorm->course/$CFG->moddata/scorm/$scorm->id/"); + @rmdir("$CFG->dataroot/$scorm->course/$CFG->moddata/scorm/"); + @rmdir("$CFG->dataroot/$scorm->course/$CFG->moddata/"); + } + $DB->set_debug($olddebug); // reset debug level + $rs->close(); + } + + /// scorm savepoint reached + upgrade_mod_savepoint($result, 2008090304, 'scorm'); + } + return $result; } diff --git a/mod/scorm/lib.php b/mod/scorm/lib.php index 51a53979ebd..5042c38930b 100755 --- a/mod/scorm/lib.php +++ b/mod/scorm/lib.php @@ -1,72 +1,93 @@ pkgtype = $packagedata->pkgtype; - $scorm->datadir = $packagedata->datadir; - $scorm->launch = $packagedata->launch; - $scorm->parse = 1; + $cmid = $scorm->coursemodule; + $cmidnumber = $scorm->cmidnumber; + $courseid = $scorm->course; - $scorm->timemodified = time(); - if (!scorm_external_link($scorm->reference)) { - $scorm->md5hash = md5_file($CFG->dataroot.'/'.$scorm->course.'/'.$scorm->reference); - } else { - $scorm->dir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'; - $scorm->md5hash = md5_file($scorm->dir.$scorm->datadir.'/'.basename($scorm->reference)); - } + $context = get_context_instance(CONTEXT_MODULE, $cmid); - $scorm = scorm_option2text($scorm); - $scorm->width = str_replace('%','',$scorm->width); - $scorm->height = str_replace('%','',$scorm->height); + $scorm = scorm_option2text($scorm); + $scorm->width = (int)str_replace('%', '', $scorm->width); + $scorm->height = (int)str_replace('%', '', $scorm->height); - //sanitize submitted values a bit - $scorm->width = clean_param($scorm->width, PARAM_INT); - $scorm->height = clean_param($scorm->height, PARAM_INT); - - if (!isset($scorm->whatgrade)) { - $scorm->whatgrade = 0; - } - $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; - - $id = $DB->insert_record('scorm', $scorm); - - if (scorm_external_link($scorm->reference) || ((basename($scorm->reference) != 'imsmanifest.xml') && ($scorm->reference[0] != '#'))) { - // Rename temp scorm dir to scorm id - $scorm->dir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'; - if (file_exists($scorm->dir.'/'.$id)) { - //delete directory as it shouldn't exist! - most likely there from an old moodle install with old files in dataroot - scorm_delete_files($scorm->dir.'/'.$id); - } - rename($scorm->dir.$scorm->datadir,$scorm->dir.'/'.$id); - } - - // Parse scorm manifest - if ($scorm->parse == 1) { - $scorm->id = $id; - $scorm->launch = scorm_parse($scorm); - $DB->set_field('scorm', 'launch', $scorm->launch, array('id'=>$scorm->id)); - } - - scorm_grade_item_update($scorm); - - return $id; - } else { - print_error('badpackage','scorm'); + if (!isset($scorm->whatgrade)) { + $scorm->whatgrade = 0; } + $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; + + if (!$id = $DB->insert_record('scorm', $scorm)) { + return false; + } + +/// update course module record - from now on this instance properly exists and all function may be used + if (!$DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid))) { + print_error('cannotaddcoursemodule'); + } + +/// reload scorm instance + $scorm = $DB->get_record('scorm', array('id'=>$id)); + +/// store the package and verify + if ($scorm->scormtype === SCORM_TYPE_LOCAL) { + if ($mform) { + $filename = $mform->get_new_filename('packagefile'); + if ($filename !== false) { + $fs = get_file_storage(); + $fs->delete_area_files($context->id, 'scorm_package'); + $mform->save_stored_file('packagefile', $context->id, 'scorm_package', 0, '/', $filename); + $scorm->reference = $filename; + } + } + + } else if ($scorm->scormtype === SCORM_TYPE_LOCALSYNC) { + $scorm->reference = $scorm->packageurl; + + } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL) { + $scorm->reference = $scorm->packageurl; + + } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY) { + $scorm->reference = $scorm->packageurl; + + } else { + return false; + } + + // save reference + $DB->update_record('scorm', $scorm); + + +/// extra fields required in grade related functions + $scorm->course = $courseid; + $scorm->cmidnumber = $cmidnumber; + $scorm->cmid = $cmid; + + scorm_parse($scorm, true); + + scorm_grade_item_update($scorm); + + return $scorm->id; } /** @@ -74,65 +95,73 @@ function scorm_add_instance($scorm) { * (defined by the form in mod_form.php) this function * will update an existing instance with new data. * -* @param mixed $scorm Form data -* @return int +* @param object $scorm Form data +* @param object $mform +* @return bool success */ -function scorm_update_instance($scorm) { +function scorm_update_instance($scorm, $mform=null) { global $CFG, $DB; require_once('locallib.php'); - $scorm->parse = 0; - if (($packagedata = scorm_check_package($scorm)) != null) { - $scorm->pkgtype = $packagedata->pkgtype; - if ($packagedata->launch == 0) { - $scorm->launch = $packagedata->launch; - $scorm->datadir = $packagedata->datadir; - $scorm->parse = 1; - if (!scorm_external_link($scorm->reference) && $scorm->reference[0] != '#') { //dont set md5hash if this is from a repo. - $scorm->md5hash = md5_file($CFG->dataroot.'/'.$scorm->course.'/'.$scorm->reference); - } elseif($scorm->reference[0] != '#') { //dont set md5hash if this is from a repo. - $scorm->dir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'; - $scorm->md5hash = md5_file($scorm->dir.$scorm->datadir.'/'.basename($scorm->reference)); - } - } - } + $cmid = $scorm->coursemodule; + $cmidnumber = $scorm->cmidnumber; + $courseid = $scorm->course; - $scorm->timemodified = time(); $scorm->id = $scorm->instance; + $context = get_context_instance(CONTEXT_MODULE, $cmid); + + if ($scorm->scormtype === SCORM_TYPE_LOCAL) { + if ($mform) { + $filename = $mform->get_new_filename('packagefile'); + if ($filename !== false) { + $scorm->reference = $filename; + $fs = get_file_storage(); + $fs->delete_area_files($context->id, 'scorm_package'); + $mform->save_stored_file('packagefile', $context->id, 'scorm_package', 0, '/', $filename); + } + } + + } else if ($scorm->scormtype === SCORM_TYPE_LOCALSYNC) { + $scorm->reference = $scorm->packageurl; + + } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL) { + $scorm->reference = $scorm->packageurl; + + } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY) { + $scorm->reference = $scorm->packageurl; + + } else { + return false; + } + $scorm = scorm_option2text($scorm); - $scorm->width = str_replace('%','',$scorm->width); - $scorm->height = str_replace('%','',$scorm->height); + $scorm->width = (int)str_replace('%','',$scorm->width); + $scorm->height = (int)str_replace('%','',$scorm->height); + $scorm->timemodified = time(); if (!isset($scorm->whatgrade)) { $scorm->whatgrade = 0; } - $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; + $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; - // Check if scorm manifest needs to be reparsed - if ($scorm->parse == 1) { - $scorm->dir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'; - if (is_dir($scorm->dir.'/'.$scorm->id)) { - scorm_delete_files($scorm->dir.'/'.$scorm->id); - } - if (isset($scorm->datadir) && ($scorm->datadir != $scorm->id) && - (scorm_external_link($scorm->reference) || ((basename($scorm->reference) != 'imsmanifest.xml') && ($scorm->reference[0] != '#')))) { - rename($scorm->dir.$scorm->datadir,$scorm->dir.'/'.$scorm->id); - } - - $scorm->launch = scorm_parse($scorm); - } else { - $oldscorm = $DB->get_record('scorm', array('id'=>$scorm->id)); - $scorm->reference = $oldscorm->reference; // This fix a problem with Firefox when the teacher choose Cancel on overwrite question - } - - if ($result = $DB->update_record('scorm', $scorm)) { - scorm_grade_item_update(stripslashes_recursive($scorm)); - //scorm_grade_item_update($scorm); // John Macklins fix - dont think this is needed + if (!$DB->update_record('scorm', $scorm)) { + return false; } - return $result; + $scorm = $DB->get_record('scorm', array('id'=>$scorm->id)); + +/// extra fields required in grade related functions + $scorm->course = $courseid; + $scorm->idnumber = $cmidnumber; + $scorm->cmid = $cmid; + + scorm_parse($scorm, (bool)$scorm->updatefreq); + + scorm_grade_item_update($scorm); + + return true; } /** @@ -152,13 +181,6 @@ function scorm_delete_instance($id) { $result = true; - $scorm->dir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'; - if (is_dir($scorm->dir.'/'.$scorm->id)) { - // Delete any dependent files - require_once('locallib.php'); - scorm_delete_files($scorm->dir.'/'.$scorm->id); - } - // Delete any dependent records if (! $DB->delete_records('scorm_scoes_track', array('scormid'=>$scorm->id))) { $result = false; @@ -168,7 +190,7 @@ function scorm_delete_instance($id) { if (! $DB->delete_records('scorm_scoes_data', array('scoid'=>$sco->id))) { $result = false; } - } + } $DB->delete_records('scorm_scoes', array('scorm'=>$scorm->id)); } else { $result = false; @@ -197,10 +219,10 @@ function scorm_delete_instance($id) { } if (! $DB->delete_records('scorm_sequencing_ruleconditions', array('scormid'=>$scorm->id))) { $result = false; - }*/ + }*/ scorm_grade_item_delete($scorm); - + return $result; } @@ -211,11 +233,11 @@ function scorm_delete_instance($id) { * * @param int $course Course id * @param int $user User id -* @param int $mod +* @param int $mod * @param int $scorm The scorm id * @return mixed */ -function scorm_user_outline($course, $user, $mod, $scorm) { +function scorm_user_outline($course, $user, $mod, $scorm) { global $CFG; require_once('locallib.php'); @@ -230,7 +252,7 @@ function scorm_user_outline($course, $user, $mod, $scorm) { * * @param int $course Course id * @param int $user User id -* @param int $mod +* @param int $mod * @param int $scorm The scorm id * @return boolean */ @@ -244,7 +266,7 @@ function scorm_user_complete($course, $user, $mod, $scorm) { $lastmodify = 0; $sometoreport = false; $report = ''; - + if ($orgs = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''),'id','id,identifier,title')) { if (count($orgs) <= 1) { unset($orgs); @@ -263,7 +285,7 @@ function scorm_user_complete($course, $user, $mod, $scorm) { $conditions['scorm'] = $scorm->id; if ($scoes = $DB->get_records('scorm_scoes', $conditions, "id ASC")){ // drop keys so that we can access array sequentially - $scoes = array_values($scoes); + $scoes = array_values($scoes); $level=0; $sublist=1; $parents[$level]='/'; @@ -336,7 +358,7 @@ function scorm_user_complete($course, $user, $mod, $scorm) { } } $report .= "\t\t\t\n"; - } + } } else { $report .= " $sco->title\n"; } @@ -380,7 +402,8 @@ function scorm_cron () { require_once('locallib.php'); $sitetimezone = $CFG->timezone; - /// Now see if there are any digest mails waiting to be sent, and if we should send them + /// Now see if there are any scorm updates to be done + if (!isset($CFG->scorm_updatetimelast)) { // To catch the first time set_config('scorm_updatetimelast', 0); } @@ -395,11 +418,8 @@ function scorm_cron () { mtrace('Updating scorm packages which require daily update');//We are updating $scormsupdate = $DB->get_records('scorm', array('updatefreq'=>UPDATE_EVERYDAY)); - if (!empty($scormsupdate)) { - foreach($scormsupdate as $scormupdate) { - $scormupdate->instance = $scormupdate->id; - $id = scorm_update_instance($scormupdate); - } + foreach($scormsupdate as $scormupdate) { + scorm_parse($scormupdate, true); } } @@ -514,7 +534,7 @@ function scorm_grade_item_update($scorm, $grades=NULL) { if (isset($scorm->cmidnumber)) { $params['idnumber'] = $scorm->cmidnumber; } - + if (($scorm->grademethod % 10) == 0) { // GRADESCOES if ($maxgrade = $DB->count_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty()))) { $params['gradetype'] = GRADE_TYPE_VALUE; @@ -559,7 +579,14 @@ function scorm_get_post_actions() { } function scorm_option2text($scorm) { - global $SCORM_POPUP_OPTIONS; + $SCORM_POPUP_OPTIONS = array('resizable'=>1, + 'scrollbars'=>1, + 'directories'=>0, + 'location'=>0, + 'menubar'=>0, + 'toolbar'=>0, + 'status'=>0); + if (isset($scorm->popup)) { if ($scorm->popup == 1) { $optionlist = array(); @@ -569,11 +596,11 @@ function scorm_option2text($scorm) { } else { $optionlist[] = $name.'=0'; } - } + } $scorm->options = implode(',', $optionlist); } else { $scorm->options = ''; - } + } } else { $scorm->popup = 0; $scorm->options = ''; @@ -656,4 +683,131 @@ function scorm_get_extra_capabilities() { return array('moodle/site:accessallgroups'); } +/** + * Lists all file areas current user may browse + */ +function scorm_get_file_areas($course, $cm, $context) { + $areas = array(); + if (has_capability('moodle/course:managefiles', $context)) { + $areas['scorm_intro'] = get_string('areaintro', 'scorm'); + $areas['scorm_content'] = get_string('areacontent', 'scorm'); + $areas['scorm_package'] = get_string('areapackage', 'scorm'); + } + return $areas; +} + +/** + * File browsing support + */ +function scorm_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { + global $CFG; + + if (!has_capability('moodle/course:managefiles', $context)) { + return null; + } + + // no writing for now! + + $fs = get_file_storage(); + + if ($filearea === 'scorm_content') { + + $filepath = is_null($filepath) ? '/' : $filepath; + $filename = is_null($filename) ? '.' : $filename; + + $urlbase = $CFG->wwwroot.'/pluginfile.php'; + if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) { + if ($filepath === '/' and $filename === '.') { + $storedfile = new virtual_root_file($context->id, $filearea, 0); + } else { + // not found + return null; + } + } + class scorm_package_file_info extends file_info_stored { + public function get_parent() { + if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { + return $this->browser->get_file_info($this->context); + } + return parent::get_parent(); + } + public function get_visible_name() { + if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { + return $this->areavisiblename; + } + return parent::get_visible_name(); + } + } + return new scorm_package_file_info($browser, $context, $storedfile, $urlbase, $areas[$filearea], true, true, false); + + } else if ($filearea === 'scorm_package') { + $filepath = is_null($filepath) ? '/' : $filepath; + $filename = is_null($filename) ? '.' : $filename; + + $urlbase = $CFG->wwwroot.'/pluginfile.php'; + if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) { + if ($filepath === '/' and $filename === '.') { + $storedfile = new virtual_root_file($context->id, $filearea, 0); + } else { + // not found + return null; + } + } + return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, false); + } + + // scorm_intro handled in file_browser + + return false; +} + +/** + * Serves scorm content, introduction images and packages. Implements needed access control ;-) + */ +function scorm_pluginfile($course, $cminfo, $context, $filearea, $args) { + global $CFG; + + if (!$cminfo->uservisible) { + return false; // probably hidden + } + + $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400; + + if ($filearea === 'scorm_intro') { + // all users may access it + $relativepath = '/'.implode('/', $args); + $fullpath = $context->id.'scorm_intro0'.$relativepath; + + $fs = get_file_storage(); + if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { + return false; + } + + } else if ($filearea === 'scorm_content') { + $revision = (int)array_shift($args); // prevents caching problems - ignored here + $relativepath = '/'.implode('/', $args); + $fullpath = $context->id.'scorm_content0'.$relativepath; + // TODO: add any other access restrictions here if needed! + + } else if ($filearea === 'scorm_package') { + if (!has_capability('moodle/course:manageactivities', $context)) { + return false; + } + $relativepath = '/'.implode('/', $args); + $fullpath = $context->id.'scorm_package0'.$relativepath; + $lifetime = 0; // no caching here + + } else { + return false; + } + + $fs = get_file_storage(); + if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { + return false; + } + + // finally send the file + send_stored_file($file, $lifetime, 0, false); +} + ?> diff --git a/mod/scorm/loadSCO.php b/mod/scorm/loadSCO.php index 8de4742a694..b60e9554dd7 100755 --- a/mod/scorm/loadSCO.php +++ b/mod/scorm/loadSCO.php @@ -2,8 +2,8 @@ require_once('../../config.php'); require_once('locallib.php'); - $id = optional_param('id', '', PARAM_INT); // Course Module ID, or - $a = optional_param('a', '', PARAM_INT); // scorm ID + $id = optional_param('id', '', PARAM_INT); // Course Module ID, or + $a = optional_param('a', '', PARAM_INT); // scorm ID $scoid = required_param('scoid', PARAM_INT); // sco ID $delayseconds = 2; // Delay time before sco launch, used to give time to browser to define API @@ -33,6 +33,9 @@ } require_login($course->id, false, $cm); + + $context = get_context_instance(CONTEXT_MODULE, $cm->id); + if (!empty($scoid)) { // // Direct SCO request @@ -60,7 +63,7 @@ $value = 'completed'; $result = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $element, $value); } - + // // Forge SCO URL // @@ -76,7 +79,7 @@ $sco->parameters = substr($sco->parameters,1); } } - + if ($version == 'AICC') { if (isset($sco->parameters) && (!empty($sco->parameters))) { $sco->parameters = '&'. $sco->parameters; @@ -89,31 +92,26 @@ $launcher = $sco->launch; } } - + if (scorm_external_link($sco->launch)) { - // Remote learning activity + //TODO: does this happen? $result = $launcher; - } else if ($scorm->reference[0] == '#') { + + } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL) { + // Remote learning activity + $result = dirname($scorm->reference).'/'.$launcher; + + } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY) { // Repository - $result = $CFG->repositorywebroot.substr($scorm->reference,1).'/'.$sco->launch; - } else { - if ((basename($scorm->reference) == 'imsmanifest.xml') && scorm_external_link($scorm->reference)) { - // Remote manifest - $result = dirname($scorm->reference).'/'.$launcher; - } else { - // Moodle internal package/manifest or remote (auto-imported) package - if (basename($scorm->reference) == 'imsmanifest.xml') { - $basedir = dirname($scorm->reference); - } else { - $basedir = $CFG->moddata.'/scorm/'.$scorm->id; - } - //note: do not convert this to use get_file_url()! - // SCORM does not work without slasharguments anyway and there might be some extra ?xx=yy params - // see MDL-16060 - $result = $CFG->wwwroot.'/file.php/'.$scorm->course.'/'.$basedir.'/'.$launcher; - } + $result = $CFG->repositorywebroot.substr($scorm->reference, 1).'/'.$sco->launch; + + } else if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) { + //note: do not convert this to use get_file_url()! + // SCORM does not work without slasharguments anyway and there might be some extra ?xx=yy params + // see MDL-16060 + $result = "$CFG->wwwroot/pluginfile.php/$context->id/scorm_content/$scorm->revision/$launcher"; } - + $scormpixdir = $CFG->modpixpath.'/scorm/pix'; ?> @@ -134,11 +132,11 @@ } }, 1000); } - //]]> + //]]> +

.  

@@ -146,5 +144,5 @@ add_to_log($course->id, 'scorm', 'launch', 'view.php?id='.$cm->id, $result, $cm->id); } ?> - + diff --git a/mod/scorm/locallib.php b/mod/scorm/locallib.php index 727c846916f..7fecd22debd 100755 --- a/mod/scorm/locallib.php +++ b/mod/scorm/locallib.php @@ -1,5 +1,7 @@ dirroot/mod/scorm/lib.php"); + /// Constants and settings for module scorm define('UPDATE_NEVER', '0'); define('UPDATE_ONCHANGE', '1'); @@ -14,122 +16,129 @@ define('GRADESCOES', '0'); define('GRADEHIGHEST', '1'); define('GRADEAVERAGE', '2'); define('GRADESUM', '3'); -$SCORM_GRADE_METHOD = array (GRADESCOES => get_string('gradescoes', 'scorm'), - GRADEHIGHEST => get_string('gradehighest', 'scorm'), - GRADEAVERAGE => get_string('gradeaverage', 'scorm'), - GRADESUM => get_string('gradesum', 'scorm')); define('HIGHESTATTEMPT', '0'); define('AVERAGEATTEMPT', '1'); define('FIRSTATTEMPT', '2'); define('LASTATTEMPT', '3'); -$SCORM_WHAT_GRADE = array (HIGHESTATTEMPT => get_string('highestattempt', 'scorm'), - AVERAGEATTEMPT => get_string('averageattempt', 'scorm'), - FIRSTATTEMPT => get_string('firstattempt', 'scorm'), - LASTATTEMPT => get_string('lastattempt', 'scorm')); -$SCORM_POPUP_OPTIONS = array('resizable'=>1, - 'scrollbars'=>1, - 'directories'=>0, - 'location'=>0, - 'menubar'=>0, - 'toolbar'=>0, - 'status'=>0); -$stdoptions = ''; -foreach ($SCORM_POPUP_OPTIONS as $popupopt => $value) { - $stdoptions .= $popupopt.'='.$value; - if ($popupopt != 'status') { - $stdoptions .= ','; - } -} - -if (!isset($CFG->scorm_maxattempts)) { - set_config('scorm_maxattempts','6'); -} - -if (!isset($CFG->scorm_frameheight)) { - set_config('scorm_frameheight','500'); -} - -if (!isset($CFG->scorm_framewidth)) { - set_config('scorm_framewidth','100%'); -} - -if (!isset($CFG->scorm_updatetime)) { - set_config('scorm_updatetime','2'); -} - -if (!isset($CFG->scorm_advancedsettings)) { - set_config('scorm_advancedsettings','0'); -} - -if (!isset($CFG->scorm_windowsettings)) { - set_config('scorm_windowsettings','0'); -} /// Local Library of functions for module scorm /** -* This function will permanently delete the given -* directory and all files and subdirectories. -* -* @param string $directory The directory to remove -* @return boolean -*/ -function scorm_delete_files($directory) { - if (is_dir($directory)) { - $files=scandir($directory); - set_time_limit(0); - foreach($files as $file) { - if (($file != '.') && ($file != '..')) { - if (!is_dir($directory.'/'.$file)) { - unlink($directory.'/'.$file); + * Extracts scrom package, sets up all variables. + * Called whenever scorm changes + * @param object $scorm instance - fields are updated and changes saved into database + * @param bool $full force full update if true + * @return void + */ +function scorm_parse($scorm, $full) { + global $CFG, $DB; + + if (!isset($scorm->cmid)) { + $cm = get_coursemodule_from_instance('scorm', $scorm->id); + $scorm->cmid = $cm->id; + } + $context = get_context_instance(CONTEXT_MODULE, $scorm->cmid); + $newhash = $scorm->sha1hash; + + if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) { + + $fs = get_file_storage(); + $packagefile = false; + + if ($scorm->scormtype === SCORM_TYPE_LOCAL) { + if ($packagefile = $fs->get_file($context->id, 'scorm_package', 0, '/', $scorm->reference)) { + $newhash = $packagefile->get_contenthash(); + } else { + $newhash = null; + } + } else { + if (!$CFG->scorm_allowtypelocalsync) { + // sorry - localsync disabled + return; + } + if ($scorm->reference !== '' and (!$full or $scorm->sha1hash !== sha1($scorm->reference))) { + $fs->delete_area_files($context->id, 'scorm_package'); + $file_record = array('contextid'=>$context->id, 'filearea'=>'scorm_package', 'itemid'=>0, 'filepath'=>'/'); + if ($packagefile = $fs->create_file_from_url($file_record, $scorm->reference)) { + $newhash = sha1($scorm->reference); } else { - scorm_delete_files($directory.'/'.$file); + $newhash = null; } } } - rmdir($directory); - return true; - } - return false; -} -/** -* Create a new temporary subdirectory with a random name in the given path -* -* @param string $strpath The scorm data directory -* @return string/boolean -*/ -function scorm_tempdir($strPath) -{ - global $CFG; - - if (is_dir($strPath)) { - do { - // Create a random string of 8 chars - $randstring = NULL; - $lchar = ''; - $len = 8; - for ($i=0; $i<$len; $i++) { - $char = chr(rand(48,122)); - while (!ereg('[a-zA-Z0-9]', $char)){ - if ($char == $lchar) continue; - $char = chr(rand(48,90)); + if ($packagefile) { + if (!$full and $packagefile and $scorm->sha1hash === $newhash) { + if (strpos($scorm->version, 'SCORM') !== false) { + if ($fs->get_file($context->id, 'scorm_content', 0, '/', 'imsmanifest.xml')) { + // no need to update + return; } - $randstring .= $char; - $lchar = $char; - } - $datadir='/'.$randstring; - } while (file_exists($strPath.$datadir)); - mkdir($strPath.$datadir, $CFG->directorypermissions); - @chmod($strPath.$datadir, $CFG->directorypermissions); // Just in case mkdir didn't do it - return $strPath.$datadir; + } else if (strpos($scorm->version, 'AICC') !== false) { + // TODO: add more sanity checks - something really exists in scorm_content area + return; + } + } + + // now extract files + $fs->delete_area_files($context->id, 'scorm_content'); + + $packer = get_file_packer('application/zip'); + $packagefile->extract_to_storage($packer, $context->id, 'scorm_content', 0, '/'); + + } else if (!$full) { + return; + } + + + if ($manifest = $fs->get_file($context->id, 'scorm_content', 0, '/', 'imsmanifest.xml')) { + require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); + // SCORM + if (!scorm_parse_scorm($scorm, $manifest)) { + $scorm->version = 'ERROR'; + } + } else { + require_once("$CFG->dirroot/mod/scorm/datamodels/aicclib.php"); + // AICC + if (!scorm_parse_aicc($scorm)) { + $scorm->version = 'ERROR'; + } + } + + } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL and $CFG->scorm_allowtypeexternal) { + if (!$full and $scorm->sha1hash === sha1($scorm->reference)) { + return; + } + require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); + // SCORM only, AICC can not be external + if (!scorm_parse_scorm($scorm, $scorm->reference)) { + $scorm->version = 'ERROR'; + } + $newhash = sha1($scorm->reference); + + } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY and !empty($CFG->repositoryactivate) and $CFG->scorm_allowtypeimsrepository) { + if (!$full and $scorm->sha1hash === sha1($scorm->reference)) { + return; + } + require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); + if (!scorm_parse_scorm($scorm, $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml')) { + $scorm->version = 'ERROR'; + } + $newhash = sha1($scorm->reference); + } else { - return false; + // sorry, disabled type + return; } + + $scorm->revision++; + $scorm->sha1hash = $newhash; + $DB->update_record('scorm', $scorm); } + function scorm_array_search($item, $needle, $haystacks, $strict=false) { if (!empty($haystacks)) { foreach ($haystacks as $key => $element) { @@ -189,7 +198,7 @@ function scorm_get_sco($id,$what=SCO_ALL) { $sco->{$scodata->name} = $scodata->value; } } else if (($what != SCO_ONLY) && (!($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id))))) { - $sco->parameters = ''; + $sco->parameters = ''; } return $sco; } else { @@ -247,10 +256,10 @@ function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) { $track->timemodified = time(); $id = $DB->insert_record('scorm_scoes_track',$track); } - + // MDL-9552, update the gradebook everything raw score is sent // Scoring by learning objects also needs to be included in the gradebook update - if (strstr($element, '.score.raw') || + if (strstr($element, '.score.raw') || (($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && ($track->value == 'completed' || $track->value == 'passed'))) { $scorm = $DB->get_record('scorm', array('id' => $scormid)); $grademethod = $scorm->grademethod % 10; @@ -259,7 +268,7 @@ function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) { scorm_update_grades($scorm, $userid); } } - + return $id; } @@ -276,7 +285,7 @@ function scorm_get_tracks($scoid,$userid,$attempt='') { } if ($tracks = $DB->get_records('scorm_scoes_track', array('userid'=>$userid, 'scoid'=>$scoid, 'attempt'=>$attempt),'element ASC')) { $usertrack->userid = $userid; - $usertrack->scoid = $scoid; + $usertrack->scoid = $scoid; // Defined in order to unify scorm1.2 and scorm2004 $usertrack->score_raw = ''; $usertrack->status = ''; @@ -291,27 +300,27 @@ function scorm_get_tracks($scoid,$userid,$attempt='') { case 'cmi.completion_status': if ($track->value == 'not attempted') { $track->value = 'notattempted'; - } + } $usertrack->status = $track->value; - break; + break; case 'cmi.core.score.raw': case 'cmi.score.raw': $usertrack->score_raw = $track->value; - break; + break; case 'cmi.core.session_time': case 'cmi.session_time': $usertrack->session_time = $track->value; - break; + break; case 'cmi.core.total_time': case 'cmi.total_time': $usertrack->total_time = $track->value; - break; - } + break; + } if (isset($track->timemodified) && ($track->timemodified > $usertrack->timemodified)) { $usertrack->timemodified = $track->timemodified; - } + } } - if (is_array($usertrack)) { + if (is_array($usertrack)) { ksort($usertrack); } return $usertrack; @@ -330,13 +339,13 @@ function scorm_get_user_data($userid) { function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) { global $DB; - $attemptscore = NULL; + $attemptscore = NULL; $attemptscore->scoes = 0; $attemptscore->values = 0; $attemptscore->max = 0; $attemptscore->sum = 0; $attemptscore->lastmodify = 0; - + if (!$scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) { return NULL; } @@ -346,11 +355,11 @@ function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) { // and 1s are grademethod $grademethod = $scorm->grademethod % 10; - foreach ($scoes as $sco) { + foreach ($scoes as $sco) { if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { if (($userdata->status == 'completed') || ($userdata->status == 'passed')) { $attemptscore->scoes++; - } + } if (!empty($userdata->score_raw)) { $attemptscore->values++; $attemptscore->sum += $userdata->score_raw; @@ -360,23 +369,23 @@ function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) { } else { $attemptscore->lastmodify = 0; } - } - } + } + } } switch ($grademethod) { case GRADEHIGHEST: $score = $attemptscore->max; - break; + break; case GRADEAVERAGE: if ($attemptscore->values > 0) { $score = $attemptscore->sum/$attemptscore->values; } else { $score = 0; - } - break; + } + break; case GRADESUM: $score = $attemptscore->sum; - break; + break; case GRADESCOES: $score = $attemptscore->scoes; break; @@ -410,7 +419,7 @@ function scorm_grade_user($scorm, $userid, $time=false) { switch ($whatgrade) { case FIRSTATTEMPT: return scorm_grade_user_attempt($scorm, $userid, 1, $time); - break; + break; case LASTATTEMPT: return scorm_grade_user_attempt($scorm, $userid, scorm_get_last_attempt($scorm->id, $userid), $time); break; @@ -502,7 +511,7 @@ function scorm_course_format_display($user,$course) { echo '

'; if ($scorms = get_all_instances_in_course('scorm', $course)) { - // The module SCORM activity with the least id is the course + // The module SCORM activity with the least id is the course $scorm = current($scorms); if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id)) { print_error('invalidcoursemodule'); @@ -528,7 +537,7 @@ function scorm_course_format_display($user,$course) { $headertext .= ''.get_string('noreports','scorm'); } $colspan = ' colspan="2"'; - } + } $headertext .= ''.format_text(get_string('summary').':
'.$scorm->summary).''; print_simple_box($headertext,'','100%'); scorm_view_display($user, $scorm, 'view.php?id='.$course->id, $cm, '100%'); @@ -546,11 +555,8 @@ function scorm_course_format_display($user,$course) { function scorm_view_display ($user, $scorm, $action, $cm, $boxwidth='') { global $CFG, $DB; - if ($scorm->updatefreq == UPDATE_EVERYTIME){ - require_once($CFG->dirroot.'/mod/scorm/lib.php'); - - $scorm->instance = $scorm->id; - scorm_update_instance($scorm); + if ($scorm->updatefreq == UPDATE_EVERYTIME) { + scorm_parse($scorm, false); } $organization = optional_param('organization', '', PARAM_INT); @@ -637,9 +643,9 @@ function scorm_simple_play($scorm,$user) { global $DB; $result = false; - + $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty())); - + if ($scoes && (count($scoes) == 1)) { if ($scorm->skipview >= 1) { $sco = current($scoes); @@ -675,356 +681,26 @@ function scorm_simple_play($scorm,$user) { return $result; } */ -function scorm_parse($scorm) { - global $CFG; - - if ($scorm->reference[0] == '#') { - if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { - $referencedir = $CFG->repository.substr($scorm->reference,1); - } - } else { - if ((!scorm_external_link($scorm->reference)) && (basename($scorm->reference) == 'imsmanifest.xml')) { - $referencedir = $CFG->dataroot.'/'.$scorm->course.'/'.$scorm->datadir; - } else { - $referencedir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm/'.$scorm->id; - } - } - - // Parse scorm manifest - if ($scorm->pkgtype == 'AICC') { - require_once('datamodels/aicclib.php'); - $scorm->launch = scorm_parse_aicc($referencedir, $scorm->id); - } else { - require_once('datamodels/scormlib.php'); - $scorm->launch = scorm_parse_scorm($referencedir,$scorm->id); - } - return $scorm->launch; -} - -/** -* Given a manifest path, this function will check if the manifest is valid -* -* @param string $manifest The manifest file -* @return object -*/ -function scorm_validate_manifest($manifest) { - $validation = new stdClass(); - if (is_file($manifest)) { - $validation->result = true; - } else { - $validation->result = false; - $validation->errors['reference'] = get_string('nomanifest','scorm'); - } - return $validation; -} - -/** -* Given a aicc package directory, this function will check if the course structure is valid -* -* @param string $packagedir The aicc package directory path -* @return object -*/ -function scorm_validate_aicc($packagedir) { - $validation = new stdClass(); - $validation->result = false; - if (is_dir($packagedir)) { - if ($handle = opendir($packagedir)) { - while (($file = readdir($handle)) !== false) { - $ext = substr($file,strrpos($file,'.')); - if (strtolower($ext) == '.cst') { - $validation->result = true; - break; - } - } - closedir($handle); - } - } - if ($validation->result == false) { - $validation->errors['reference'] = get_string('nomanifest','scorm'); - } - return $validation; -} - - -function scorm_validate($data) { - global $CFG, $DB; - - $validation = new stdClass(); - $validation->errors = array(); - - if (!isset($data['course']) || empty($data['course'])) { - $validation->errors['reference'] = get_string('missingparam','scorm'); - $validation->result = false; - return $validation; - } - $courseid = $data['course']; // Course Module ID - - if (!isset($data['reference']) || empty($data['reference'])) { - $validation->errors['reference'] = get_string('packagefile','scorm'); - $validation->result = false; - return $validation; - } - $reference = $data['reference']; // Package/manifest path/location - - $scormid = $data['instance']; // scorm ID - $scorm = new stdClass(); - if (!empty($scormid)) { - if (!$scorm = $DB->get_record('scorm', array('id'=>$scormid))) { - $validation->errors['reference'] = get_string('missingparam','scorm'); - $validation->result = false; - return $validation; - } - } - - if ($reference[0] == '#') { - if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { - $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml'; - } else { - $validation->errors['reference'] = get_string('badpackage','scorm'); - $validation->result = false; - return $validation; - } - } else if (!scorm_external_link($reference)) { - $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference; - } - - // Create a temporary directory to unzip package or copy manifest and validate package - $tempdir = ''; - $scormdir = ''; - if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { - if ($tempdir = scorm_tempdir($scormdir)) { - $localreference = $tempdir.'/'.basename($reference); - copy ("$reference", $localreference); - if (!is_file($localreference)) { - $validation->errors['reference'] = get_string('badpackage','scorm'); - $validation->result = false; - } else { - $ext = strtolower(substr(basename($localreference),strrpos(basename($localreference),'.'))); - switch ($ext) { - case '.pif': - case '.zip': - if (!unzip_file($localreference, $tempdir, false)) { - $validation->errors['reference'] = get_string('unziperror','scorm'); - $validation->result = false; - } else { - unlink ($localreference); - if (is_file($tempdir.'/imsmanifest.xml')) { - $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml'); - $validation->pkgtype = 'SCORM'; - } else { - $validation = scorm_validate_aicc($tempdir); - if (($validation->result == 'regular') || ($validation->result == 'found')) { - $validation->pkgtype = 'AICC'; - } else { - $validation->errors['reference'] = get_string('nomanifest','scorm'); - $validation->result = false; - } - } - } - break; - case '.xml': - if (basename($localreference) == 'imsmanifest.xml') { - $validation = scorm_validate_manifest($localreference); - } else { - $validation->errors['reference'] = get_string('nomanifest','scorm'); - $validation->result = false; - } - break; - default: - $validation->errors['reference'] = get_string('badpackage','scorm'); - $validation->result = false; - break; - } - } - if (is_dir($tempdir)) { - // Delete files and temporary directory - scorm_delete_files($tempdir); - } - } else { - $validation->errors['reference'] = get_string('packagedir','scorm'); - $validation->result = false; - } - } else { - $validation->errors['reference'] = get_string('datadir','scorm'); - $validation->result = false; - } - return $validation; -} - -function scorm_check_package($data) { - global $CFG, $COURSE, $DB; - - $courseid = $data->course; // Course Module ID - $reference = $data->reference; // Package path - $scormid = $data->instance; // scorm ID - - $validation = new stdClass(); - - if (!empty($courseid) && !empty($reference)) { - $externalpackage = scorm_external_link($reference); - - $validation->launch = 0; - $referencefield = $reference; - if (empty($reference)) { - $validation = null; - } else if ($reference[0] == '#') { - if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { - $referencefield = $reference.'/imsmanifest.xml'; - $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml'; - } else { - $validation = null; - } - } else if (!$externalpackage) { - $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference; - } - - if (!empty($scormid)) { - // - // SCORM Update - // - if ((!empty($validation)) && (is_file($reference) || $externalpackage)){ - - if (!$externalpackage) { - $mdcheck = md5_file($reference); - } else if ($externalpackage){ - if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { - if ($tempdir = scorm_tempdir($scormdir)) { - copy ("$reference", $tempdir.'/'.basename($reference)); - $mdcheck = md5_file($tempdir.'/'.basename($reference)); - scorm_delete_files($tempdir); - } - } - } - - if ($scorm = $DB->get_record('scorm', array('id'=>$scormid))) { - if ($scorm->reference[0] == '#') { - if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { - $oldreference = $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml'; - } else { - $oldreference = $scorm->reference; - } - } else if (!scorm_external_link($scorm->reference)) { - $oldreference = $CFG->dataroot.'/'.$courseid.'/'.$scorm->reference; - } else { - $oldreference = $scorm->reference; - } - $validation->launch = $scorm->launch; - if ((($oldreference == $reference) && ($mdcheck != $scorm->md5hash)) || ($oldreference != $reference)) { - // This is a new or a modified package - $validation->launch = 0; - } else { - // Old package already validated - if (strpos($scorm->version,'AICC') !== false) { - $validation->pkgtype = 'AICC'; - } else { - $validation->pkgtype = 'SCORM'; - } - } - } else { - $validation = null; - } - } else { - $validation = null; - } - } - //$validation->launch = 0; - if (($validation != null) && ($validation->launch == 0)) { - // - // Package must be validated - // - $ext = strtolower(substr(basename($reference),strrpos(basename($reference),'.'))); - $tempdir = ''; - switch ($ext) { - case '.pif': - case '.zip': - // Create a temporary directory to unzip package and validate package - $scormdir = ''; - if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { - if ($tempdir = scorm_tempdir($scormdir)) { - copy ("$reference", $tempdir.'/'.basename($reference)); - unzip_file($tempdir.'/'.basename($reference), $tempdir, false); - if (!$externalpackage) { - unlink ($tempdir.'/'.basename($reference)); - } - if (is_file($tempdir.'/imsmanifest.xml')) { - $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml'); - $validation->pkgtype = 'SCORM'; - } else { - $validation = scorm_validate_aicc($tempdir); - $validation->pkgtype = 'AICC'; - } - } else { - $validation = null; - } - } else { - $validation = null; - } - break; - case '.xml': - if (basename($reference) == 'imsmanifest.xml') { - if ($externalpackage) { - if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { - if ($tempdir = scorm_tempdir($scormdir)) { - copy ("$reference", $tempdir.'/'.basename($reference)); - if (is_file($tempdir.'/'.basename($reference))) { - $validation = scorm_validate_manifest($tempdir.'/'.basename($reference)); - } else { - $validation = null; - } - } - } - } else { - $validation = scorm_validate_manifest($reference); - } - $validation->pkgtype = 'SCORM'; - } else { - $validation = null; - } - break; - default: - $validation = null; - break; - } - if ($validation == null) { - if (is_dir($tempdir)) { - // Delete files and temporary directory - scorm_delete_files($tempdir); - } - } else { - if (($ext == '.xml') && (!$externalpackage)) { - $validation->datadir = dirname($referencefield); - } else { - $validation->datadir = substr($tempdir,strlen($scormdir)); - } - $validation->launch = 0; - } - } - } else { - $validation = null; - } - return $validation; -} - function scorm_get_count_users($scormid, $groupingid=null) { global $CFG, $DB; - + if (!empty($CFG->enablegroupings) && !empty($groupingid)) { $sql = "SELECT COUNT(DISTINCT st.userid) FROM {scorm_scoes_track} st INNER JOIN {groups_members} gm ON st.userid = gm.userid - INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid + INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid WHERE st.scormid = ? AND gg.groupingid = ? "; $params = array($scormid, $groupingid); } else { $sql = "SELECT COUNT(DISTINCT st.userid) - FROM {scorm_scoes_track} st + FROM {scorm_scoes_track} st WHERE st.scormid = ? "; $params = array($scormid); } - + return ($DB->count_records_sql($sql, $params)); } diff --git a/mod/scorm/mod_form.php b/mod/scorm/mod_form.php index 669cb1bd6ec..399edc37506 100644 --- a/mod/scorm/mod_form.php +++ b/mod/scorm/mod_form.php @@ -5,10 +5,22 @@ require_once($CFG->dirroot.'/mod/scorm/locallib.php'); class mod_scorm_mod_form extends moodleform_mod { function definition() { + global $CFG; - global $CFG, $COURSE, $SCORM_GRADE_METHOD, $SCORM_WHAT_GRADE; - $mform =& $this->_form; - if (isset($CFG->slasharguments) && !$CFG->slasharguments) { + $mform = $this->_form; + + $SCORM_GRADE_METHOD = array (GRADESCOES => get_string('gradescoes', 'scorm'), + GRADEHIGHEST => get_string('gradehighest', 'scorm'), + GRADEAVERAGE => get_string('gradeaverage', 'scorm'), + GRADESUM => get_string('gradesum', 'scorm')); + + $SCORM_WHAT_GRADE = array (HIGHESTATTEMPT => get_string('highestattempt', 'scorm'), + AVERAGEATTEMPT => get_string('averageattempt', 'scorm'), + FIRSTATTEMPT => get_string('firstattempt', 'scorm'), + LASTATTEMPT => get_string('lastattempt', 'scorm')); + + + if (!$CFG->slasharguments) { $mform->addElement('static', '', '',notify(get_string('slashargs', 'scorm'), 'notifyproblem', 'center', true)); } //------------------------------------------------------------------------------- @@ -29,11 +41,34 @@ class mod_scorm_mod_form extends moodleform_mod { $mform->addRule('summary', get_string('required'), 'required', null, 'client'); $mform->setHelpButton('summary', array('writing', 'questions', 'richtext'), false, 'editorhelpbutton'); +// Scorm types + $options = array(SCORM_TYPE_LOCAL => get_string('typelocal', 'scorm')); + + if ($CFG->scorm_allowtypeexternal) { + $options[SCORM_TYPE_EXTERNAL] = get_string('typeexternal', 'scorm'); + } + + if ($CFG->scorm_allowtypelocalsync) { + $options[SCORM_TYPE_LOCALSYNC] = get_string('typelocalsync', 'scorm'); + } + + if (!empty($CFG->repositoryactivate) and $CFG->scorm_allowtypeimsrepository) { + $options[SCORM_TYPE_IMSREPOSITORY] = get_string('typeimsrepository', 'scorm'); + } + + $mform->addElement('select', 'scormtype', get_string('scormtype', 'scorm'), $options); + // Reference - $mform->addElement('choosecoursefileorimsrepo', 'reference', get_string('package','scorm')); - $mform->setType('reference', PARAM_RAW); // We need to find a better PARAM - $mform->addRule('reference', get_string('required'), 'required'); - $mform->setHelpButton('reference',array('package', get_string('package', 'scorm'), 'scorm')); + if (count($options) > 1) { + $mform->addElement('text', 'packageurl', get_string('url', 'scorm'), array('size'=>60)); + $mform->setType('packageurl', PARAM_RAW); + $mform->setHelpButton('packageurl', array('packagefile', get_string('package', 'scorm'), 'scorm')); + $mform->disabledIf('packageurl', 'scormtype', 'eq', SCORM_TYPE_LOCAL); + } + +// New local package upload + $mform->addElement('file', 'packagefile', get_string('package','scorm')); + $mform->disabledIf('packagefile', 'scormtype', 'noteq', SCORM_TYPE_LOCAL); //------------------------------------------------------------------------------- // Other Settings @@ -41,7 +76,7 @@ class mod_scorm_mod_form extends moodleform_mod { // Grade Method $mform->addElement('select', 'grademethod', get_string('grademethod', 'scorm'), $SCORM_GRADE_METHOD); - $mform->setHelpButton('grademethod', array('grademethod',get_string('grademethod', 'scorm'),'scorm')); + $mform->setHelpButton('grademethod', array('grademethod', get_string('grademethod', 'scorm'),'scorm')); $mform->setDefault('grademethod', 0); // Maximum Grade @@ -50,7 +85,7 @@ class mod_scorm_mod_form extends moodleform_mod { } $mform->addElement('select', 'maxgrade', get_string('maximumgrade'), $grades); $mform->setDefault('maxgrade', 0); - $mform->disabledIf('maxgrade', 'grademethod','eq',GRADESCOES); + $mform->disabledIf('maxgrade', 'grademethod','eq', GRADESCOES); // Attempts $mform->addElement('static', '', '' ,'
'); @@ -99,12 +134,12 @@ class mod_scorm_mod_form extends moodleform_mod { $mform->addElement('static', 'stagesize', get_string('stagesize','scorm')); $mform->setHelpButton('stagesize', array('stagesize',get_string('stagesize', 'scorm'), 'scorm')); // Width - $mform->addElement('text', 'width', get_string('width','scorm'),'maxlength="5" size="5"'); + $mform->addElement('text', 'width', get_string('width','scorm'), 'maxlength="5" size="5"'); $mform->setDefault('width', $CFG->scorm_framewidth); $mform->setType('width', PARAM_INT); - + // Height - $mform->addElement('text', 'height', get_string('height','scorm'),'maxlength="5" size="5"'); + $mform->addElement('text', 'height', get_string('height','scorm'), 'maxlength="5" size="5"'); $mform->setDefault('height', $CFG->scorm_frameheight); $mform->setType('height', PARAM_INT); @@ -112,7 +147,7 @@ class mod_scorm_mod_form extends moodleform_mod { $options = array(); $options[0] = get_string('iframe', 'scorm'); $options[1] = get_string('popup', 'scorm'); - $mform->addElement('select', 'popup', get_string('display','scorm'), $options); + $mform->addElement('select', 'popup', get_string('display', 'scorm'), $options); $mform->setDefault('popup', 0); $mform->setAdvanced('popup'); @@ -211,7 +246,7 @@ class mod_scorm_mod_form extends moodleform_mod { foreach ($options as $option) { list($element,$value) = explode('=',$option); $element = trim($element); - $default_values[$element] = trim($value); + $default_values[$element] = trim($value); } } if (isset($default_values['grademethod'])) { @@ -228,7 +263,7 @@ class mod_scorm_mod_form extends moodleform_mod { $coursescorm = current($scorms); if (($COURSE->format == 'scorm') && ((count($scorms) == 0) || ($default_values['instance'] == $coursescorm->id))) { $default_values['redirect'] = 'yes'; - $default_values['redirecturl'] = '../course/view.php?id='.$default_values['course']; + $default_values['redirecturl'] = '../course/view.php?id='.$default_values['course']; } else { $default_values['redirect'] = 'no'; $default_values['redirecturl'] = '../mod/scorm/view.php?id='.$default_values['coursemodule']; @@ -244,30 +279,88 @@ class mod_scorm_mod_form extends moodleform_mod { function validation($data, $files) { $errors = parent::validation($data, $files); - $validate = scorm_validate($data); + $type = $data['scormtype']; - if (!$validate->result) { - $errors = $errors + $validate->errors; + if ($type === SCORM_TYPE_LOCAL) { + if (!empty($data['update'])) { + //ok, not required + + } else if (empty($files['packagefile'])) { + $errors['packagefile'] = get_string('required'); + + } else { + $packer = get_file_packer('application/zip'); + + $filelist = $packer->list_files($files['packagefile']); + if (!is_array($filelist)) { + $errors['packagefile'] = 'Incorrect file package - not an archive'; //TODO: localise + } else { + $manifestpresent = false; + $aiccfound = false; + foreach ($filelist as $info) { + if ($info->pathname == 'imsmanifest.xml') { + $manifestpresent = true; + break; + } + if (preg_match('/\.cst$/', $info->pathname)) { + $aiccfound = true; + break; + } + } + if (!$manifestpresent and !$aiccfound) { + $errors['packagefile'] = 'Incorrect file package - missing imsmanifest.xml or AICC structure'; //TODO: localise + } + } + } + + } else if ($type === SCORM_TYPE_EXTERNAL) { + $reference = $data['packageurl']; + if (!preg_match('/(http:\/\/|https:\/\/|www).*\/imsmanifest.xml$/i', $reference)) { + $errors['packageurl'] = get_string('required'); // TODO: improve help + } + + } else if ($type === 'packageurl') { + $reference = $data['reference']; + if (!preg_match('/(http:\/\/|https:\/\/|www).*(\.zip|\.pif)$/i', $reference)) { + $errors['packageurl'] = get_string('required'); // TODO: improve help + } + + } else if ($type === SCORM_TYPE_IMSREPOSITORY) { + $reference = $data['packageurl']; + if (stripos($reference, '#') !== 0) { + $errors['packageurl'] = get_string('required'); + } } return $errors; } - //need to translate the "options" field. + + //need to translate the "options" and "reference" field. function set_data($default_values) { - if (is_object($default_values)) { - if (!empty($default_values->options)) { - $options = explode(',', $default_values->options); - foreach ($options as $option) { - $opt = explode('=', $option); - if (isset($opt[1])) { - $default_values->$opt[0] = $opt[1]; - } + $default_values = (array)$default_values; + + if (isset($default_values['scormtype']) and isset($default_values['reference'])) { + switch ($default_values['scormtype']) { + case SCORM_TYPE_LOCALSYNC : + case SCORM_TYPE_EXTERNAL: + case SCORM_TYPE_IMSREPOSITORY: + $default_values['packageurl'] = $default_values['reference']; + } + } + unset($default_values['reference']); + + if (!empty($default_values['options'])) { + $options = explode(',', $default_values['options']); + foreach ($options as $option) { + $opt = explode('=', $option); + if (isset($opt[1])) { + $default_values[$opt][0] = $opt[1]; } } - $default_values = (array)$default_values; } + $this->data_preprocessing($default_values); - parent::set_data($default_values); //never slashed for moodleform_mod + parent::set_data($default_values); } } ?> \ No newline at end of file diff --git a/mod/scorm/settings.php b/mod/scorm/settings.php index 4d99e25cd9e..5c75fcf2c7c 100644 --- a/mod/scorm/settings.php +++ b/mod/scorm/settings.php @@ -1,9 +1,25 @@ add(new admin_setting_configtext('scorm_framewidth', get_string('width', 'scorm'), - get_string('framewidth', 'scorm'), '100%')); + get_string('framewidth', 'scorm'), 100)); $settings->add(new admin_setting_configtext('scorm_frameheight', get_string('height', 'scorm'), get_string('frameheight', 'scorm'), 500)); -?> +$settings->add(new admin_setting_configtext('scorm_maxattempts', get_string('maximumattempts', 'scorm'), + '', 6)); + +$settings->add(new admin_setting_configtext('scorm_updatetime', get_string('updatetime', 'scorm'), + '', 2)); + +$settings->add(new admin_setting_configcheckbox('scorm_allowtypeexternal', get_string('allowtypeexternal', 'scorm'), + '', 0)); + +$settings->add(new admin_setting_configcheckbox('scorm_allowtypelocalsync', get_string('allowtypelocalsync', 'scorm'), + '', 0)); + +$settings->add(new admin_setting_configcheckbox('scorm_allowtypeimsrepository', get_string('allowtypeimsrepository', 'scorm'), + '', 0)); + + + diff --git a/mod/scorm/version.php b/mod/scorm/version.php index 98ca6a37a54..3abb85dc734 100755 --- a/mod/scorm/version.php +++ b/mod/scorm/version.php @@ -6,12 +6,12 @@ ///////////////////////////////////////////////////////////////////////////////// -// NOTE The version below was accidentally set a month into the future! We need to -// catch up now, so until 27th October please only increment in very tiny steps +// NOTE The version below was accidentally set a month into the future! We need to +// catch up now, so until 27th October please only increment in very tiny steps // in HEAD, until we get past that date.. -$module->version = 2008073100; // The (date) version of this module -$module->requires = 2007101509; // The version of Moodle that is required +$module->version = 2008090304; // The (date) version of this module +$module->requires = 2008090108; // The version of Moodle that is required $module->cron = 300; // How often should cron check this module (seconds)? ?> \ No newline at end of file