From 1d68db47b2dcb00a36e5b2d90e5be3cd6494cd56 Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Fri, 7 Nov 2014 17:45:15 -0500 Subject: [PATCH] Support Opale/Scenarii by adding variable to better support SCORM 1.2 by watching over the definition, by the SCO, of the lesson_status and the call to LMSFinish() or the move to another element - refs #398 refs BT#8897 --- main/newscorm/lp_ajax_initialize.php | 3 +- main/newscorm/lp_ajax_switch_item.php | 3 +- main/newscorm/scorm_api.php | 142 ++++++++++++++++++-------- 3 files changed, 103 insertions(+), 45 deletions(-) diff --git a/main/newscorm/lp_ajax_initialize.php b/main/newscorm/lp_ajax_initialize.php index 1a3e55fa7f..a2cee911b4 100755 --- a/main/newscorm/lp_ajax_initialize.php +++ b/main/newscorm/lp_ajax_initialize.php @@ -129,7 +129,8 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item) { //"olms.item_objectives = new Array();" . "olms.item_objectives = ".$myobjectives.";" . "olms.G_lastError = 0;" . - "olms.G_LastErrorMessage = 'No error';" ; + "olms.G_LastErrorMessage = 'No error';". + "olms.finishSignalReceived = 0;"; /* * and re-initialise the rest (proper to the LMS) * -lms_lp_id diff --git a/main/newscorm/lp_ajax_switch_item.php b/main/newscorm/lp_ajax_switch_item.php index 7f628678e0..6107c56995 100755 --- a/main/newscorm/lp_ajax_switch_item.php +++ b/main/newscorm/lp_ajax_switch_item.php @@ -171,7 +171,8 @@ function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_it "olms.interactions = new Array(".$myistring.");". "olms.item_objectives = new Array();". "olms.G_lastError = 0;". - "olms.G_LastErrorMessage = 'No error';"; + "olms.G_LastErrorMessage = 'No error';". + "olms.finishSignalReceived = 0;"; /* * and re-initialise the rest * -lms_lp_id diff --git a/main/newscorm/scorm_api.php b/main/newscorm/scorm_api.php index 09fe9c2495..60577c1767 100755 --- a/main/newscorm/scorm_api.php +++ b/main/newscorm/scorm_api.php @@ -2,7 +2,7 @@ /* For licensing terms, see /license.txt */ /** - * API event handler functions for Scorm 1.1 and 1.2 and 1.3 + * API event handler functions for Scorm 1.1 and 1.2 and 1.3 (latter not fully supported) * This script is divided into three sections. * The first section (below) is the initialisation part. * The second section is the SCORM object part @@ -10,7 +10,7 @@ * and frames refresh * @author Denes Nagy (original author - 2003-2004) * @author Yannick Warnier (extended and maintained - 2005-2014) - * @version v 1.1 + * @version v 1.2 * @access public * @package chamilo.learnpath.scorm */ @@ -140,6 +140,10 @@ olms.variable_to_send=new Array(); // temporary list of variables (gets set to true when set through LMSSetValue) olms.updatable_vars_list = new Array(); +// marker of whether the LMSFinish() function was called, which is important for SCORM behaviour +olms.finishSignalReceived = 0; +// marker to remember if the SCO has calles a "set" on lesson_status +olms.statusSignalReceived = 0; // Strictly scorm variables olms.score=get_score();?>; @@ -182,6 +186,7 @@ olms.lms_item_core_exit = 'get_core_exit();?>'; olms.lms_course_id = 'get_course_int_id(); ?>'; olms.lms_session_id = ''; olms.lms_course_code = 'getCourseCode(); ?>'; +olms.lms_course_id = 'get_course_int_id(); ?>'; get_items_details_as_js('olms.lms_item_types');?> olms.asset_timer = 0; @@ -198,27 +203,33 @@ addEvent(window, 'load', addListeners, false); // Initialize stuff when the page is loaded $(document).ready(function() { - logit_lms('document.ready start'); + logit_lms('document.ready event starts'); + logit_lms('These logs are generated by the main/newscorm/scorm_api.php JS ' + + 'library when the admin has clicked on the debug icon in the ' + + 'learning paths list: ' + + 'lines prefixed with "LMS:" refer to actions taken on the LMS side, ' + + 'while lines prefixed with "SCORM:" refer to actions taken to match ' + + 'the SCORM standard at the JS level.', 3); + logit_scorm('LMSSetValue calls are shown in red for better visibility.', 0); + logit_scorm('Other SCORM calls are shown in orange.', 1); + logit_lms('To add new messages to these logs, use logit_lms() or logit_scorm().'); olms.info_lms_item[0] = 'get_id();?>'; olms.info_lms_item[1] = 'get_id();?>'; $("#content_id").load(function() { - logit_lms('#content_id on load executing: '); + logit_lms('#content_id load event starts'); olms.info_lms_item[0] = olms.info_lms_item[1]; + // Only trigger the LMSInitialize automatically if not SCO if (olms.lms_item_types['i'+olms.info_lms_item[1]] != 'sco') { LMSInitialize(); } else { - logit_lms('Cant execute LMSInitialize() (type is sco)',2); + logit_lms('Content type is SCO and is responsible to launch LMSInitialize() on its own - Skipping',2); } }); }); -//Seems that this objs are not used -//oXAJAX = new XAJAXobject(); -//oxajax = new XAJAXobject(); - // This code was moved inside LMSInitialize() if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') { xajax_start_timer(); @@ -235,7 +246,7 @@ if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type function LMSInitialize() { /* load info for this new item by calling the js_api_refresh command in * the message frame. The message frame will update the JS variables by - * itself, in JS, by doing things like top.lescsson_status = 'not attempted' + * itself, in JS, by doing things like top.lesson_status = 'not attempted' * and that kind of stuff, so when the content loads in the content frame * it will have all the correct variables set */ @@ -246,10 +257,12 @@ function LMSInitialize() { olms.G_LastErrorMessage = 'No error'; olms.lms_initialized = 0; + olms.finishSignalReceived = 0; + olms.statusSignalReceived = 0; // if there are more parameters than "" if (arguments.length > 1) { - olms.G_LastError = G_InvalidArgumentError; - olms.G_LastErrorMessage = G_InvalidArgumentErrorMessage; + olms.G_LastError = G_InvalidArgumentError; + olms.G_LastErrorMessage = G_InvalidArgumentErrorMessage; logit_scorm('Error '+ G_InvalidArgumentError + G_InvalidArgumentErrorMessage, 0); return('false'); } else { @@ -294,11 +307,13 @@ function LMSInitialize() { + '\nlms_lp_id : '+ olms.lms_lp_id + '\nlms_user_id : '+ olms.lms_user_id + '\nlms_view_id : '+ olms.lms_view_id + + '\nfinishSignalReceived : '+ olms.finishSignalReceived + + '\nstatusSignalReceived : '+ olms.statusSignalReceived ; logit_scorm('LMSInitialize() with params: '+log); - if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') { + if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') { xajax_start_timer(); } @@ -312,7 +327,7 @@ function LMSInitialize() { } else { attach_glossary_into_scorm('manual'); } - + attach_glossary_into_scorm('automatic'); return('true'); @@ -582,6 +597,7 @@ function LMSSetValue(param, val) { olms.commit = true; //value has changed, need to re-commit olms.G_LastError = G_NoError ; olms.G_LastErrorMessage = 'No error'; + return_value = 'false'; if ( param == "cmi.core.score.raw" ) { @@ -603,6 +619,7 @@ function LMSSetValue(param, val) { } else if ( param == "cmi.core.lesson_status" ) { olms.lesson_status = val; olms.updatable_vars_list['cmi.core.lesson_status'] = true; + olms.statusSignalReceived = 1; return_value='true'; } else if ( param == "cmi.completion_status" ) { olms.lesson_status = val; @@ -693,8 +710,8 @@ function LMSSetValue(param, val) { return_value='true'; break; case "correct_responses": - //do nothing yet - olms.interactions[elem_id][4].push(val); + // Add at the end of the array + olms.interactions[elem_id][4][olms.interactions[elem_id][4].length] = val; logit_scorm("Interaction "+elem_id+"'s correct_responses not updated",2); return_value='true'; break; @@ -800,12 +817,10 @@ function SetValue(param, val) { /** * Saves the current data from JS memory to the LMS database - * @param string The origin of the call to save the data ('commit','finish', 'unload' or 'terminate') - * @note origin actually seems deprecated now */ -function savedata(origin) { +function savedata() { //origin can be 'commit', 'finish' or 'terminate' (depending on the calling function) - logit_lms('function savedata() with origin: ' + origin, 3); + logit_lms('function savedata()', 3); //Status is NOT modified here see the lp_ajax_save_item.php file @@ -830,11 +845,27 @@ function savedata(origin) { //xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, old_item_id); //Modified version - xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, item_to_save); + xajax_save_item_scorm( + olms.lms_lp_id, + olms.lms_user_id, + olms.lms_view_id, + item_to_save, + olms.session_id, + olms.course_id, + olms.finishSignalReceived, + olms.userNavigatesAway, + olms.statusSignalReceived + ); olms.info_lms_item[1] = olms.lms_item_id; if (olms.item_objectives.length > 0) { - xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id, old_item_id, olms.item_objectives); + xajax_save_objectives( + olms.lms_lp_id, + olms.lms_user_id, + olms.lms_view_id, + old_item_id, + olms.item_objectives + ); } olms.execute_stats = false; //clean array @@ -854,10 +885,11 @@ function LMSCommit(val) { olms.G_LastError = G_NoError ; olms.G_LastErrorMessage = 'No error'; - savedata('commit'); + savedata(); reinit_updatable_vars_list(); - //commit = 'false' ; //now changes have been commited, no need to update until next SetValue() + //now changes have been commited, no need to update until next SetValue() + //commit = 'false' ; return('true'); } @@ -880,17 +912,16 @@ function Commit(val) { function LMSFinish(val) { olms.G_LastError = G_NoError ; olms.G_LastErrorMessage = 'No error'; + olms.finishSignalReceived = 1; // if olms.commit == false, then the SCORM didn't ask for a commit, so we // should at least report that if ( !olms.commit ) { logit_scorm('LMSFinish() (no LMSCommit())',1); } - //if ( olms.commit ) { - logit_scorm('LMSFinish() called',1); - savedata('finish'); - olms.commit = false; - //} + logit_scorm('LMSFinish() called',1); + savedata(); + olms.commit = false; //reinit the list of modified variables reinit_updatable_vars_list(); @@ -971,7 +1002,7 @@ function Terminate() { olms.G_LastError = G_NoError ; olms.G_LastErrorMessage = 'No error'; olms.commit = true; - savedata('terminate'); + savedata(); return ('true'); } } @@ -1064,7 +1095,7 @@ function lms_save_asset() { if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') { logit_lms('lms_save_asset'); logit_lms('execute_stats :'+ olms.execute_stats); - xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location, olms.interactions, olms.lms_item_core_exit, olms.lms_item_type); + xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location, olms.interactions, olms.lms_item_core_exit, olms.lms_item_type, olms.session_id, olms.course_id); if (olms.item_objectives.length>0) { xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,olms.item_objectives); } @@ -1344,7 +1375,7 @@ function switch_item(current_item, next_item){ } else { logit_lms('Case 2 - current != sco but next == sco'); } - xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, orig_item_type); + xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, olms.session_id, olms.course_id, olms.finishSignalReceived, 1, olms.statusSignalReceived); xajax_switch_item_details(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, next_item); } else { if (next_item_type != 'sco') { @@ -1352,7 +1383,7 @@ function switch_item(current_item, next_item){ } else { logit_lms('Case 4 - current == sco and next == sco'); } - xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id); + xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.session_id, olms.course_id, olms.finishSignalReceived, 1, olms.statusSignalReceived); reinit_updatable_vars_list(); xajax_switch_item_toc(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,next_item); @@ -1375,18 +1406,18 @@ function switch_item(current_item, next_item){ * because another onunload event can be triggered by the SCO itself, * which can set, for example, the status to incomplete while the * status has already been set to "completed" by the hand-made - * savedata(unload) (and then the status cannot be "incompleted" + * savedata() (and then the status cannot be "incompleted" * anymore) */ /* if (olms.lms_item_type=='sco' && olms.lesson_status != 'completed' && olms.lesson_status != 'passed' && olms.lesson_status != 'browsed' && olms.lesson_status != 'incomplete' && olms.lesson_status != 'failed') { - // savedata('finish') treats the special condition and saves the - // new status to the database, so switch_item_details() enjoys the - // new status - savedata('finish'); + // savedata() with olms.finishSignalReceived == 1 treats the special + // condition and saves the new status to the database, so + // switch_item_details() enjoys the new status + savedata(); } - xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, olms.lms_item_type); + xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, olms.session_id, olms.course_id, olms.finishSignalReceived, 1, olms.statusSignalReceived); */ olms.execute_stats = false; @@ -1513,7 +1544,10 @@ function xajax_save_item( lms_item_core_exit, item_type, session_id, - course_id) { + course_id, + finishSignalReceived = 0, + userNavigatesAway = 0, + statusSignalReceived = 0) { var params = ''; params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id; params += '&iid='+lms_item_id+'&s='+score+'&max='+max+'&min='+min; @@ -1522,6 +1556,10 @@ function xajax_save_item( params += '&core_exit='+lms_item_core_exit; params += '&session_id='+session_id; params += '&course_id='+course_id; + params += '&finishSignalReceived='+finishSignalReceived; + params += '&userNavigatesAway='+userNavigatesAway; + params += '&statusSignalReceived='+statusSignalReceived; + //console.info(session_time); if (olms.lms_lp_type == 1 || item_type == 'document') { logit_lms('xajax_save_item with params:' + params,3); @@ -1548,12 +1586,30 @@ function xajax_save_item( * @uses olms.updatable_vars_list * @uses lp_ajax_save_item.php through an AJAX call */ -function xajax_save_item_scorm(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, finish) { +function xajax_save_item_scorm( + lms_lp_id, + lms_user_id, + lms_view_id, + lms_item_id, + session_id, + course_id, + finishSignalReceived = 0, + userNavigatesAway = 0, + statusSignalReceived = 0 + ) +{ if (typeof(finish) == 'undefined') { finish = 0; } var is_interactions='false'; - var params = 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id+'&iid='+lms_item_id+'&finish='+finish; + var params = 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id+'&iid='+lms_item_id; + // The missing arguments will be ignored by lp_ajax_save_item.php + //params += '&s=&max=&min=&status=&t=&suspend=&loc=&interact=&core_exit='; + params += '&session_id='+session_id; + params += '&course_id='+course_id; + params += '&finishSignalReceived='+finishSignalReceived; + params += '&userNavigatesAway='+userNavigatesAway; + params += '&statusSignalReceived='+statusSignalReceived; var my_scorm_values = new Array(); my_scorm_values = process_scorm_values(); @@ -1628,7 +1684,7 @@ function xajax_save_item_scorm(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, /** * Starts the timer with the server clock time. * @return void - * @todo check the timer stuff really works + * @todo check the timer stuff really works and rename function to startTimer() * @uses lp_ajax_start_timer.php */ function xajax_start_timer() {