I’ve addressed handling of the LMSSetValue() and LMSGetValue(), LMSCommit(), and LMSFinish() calls. The remaining piece of the puzzle is to look at how the RTE is initialized.
In Rev 1 of the code, the course/SCO call to LMSInitialize() caused an AJAX request to be sent to the server where a script called initialize.php was executed. The one thing that this doesn’t do is to transfer all of the data element values from the database into the local cache. In theory, I could simply use one or more additional AJAX calls to transfer the data from the server to the local cache. But I think there’s a better way.
I’m going to start by modifying my api.php code by adding the following lines of code.
// read database login information and connect require "config.php"; dbConnect(); // initialize data elements in the database if they're not already set, and // dynamically create the javascript code to initialize the local cache $initializeCache = initializeSCO();
Lines 2 and 3 load the database configuration information, and connect to the database. Then, line 7 calls a function in subs.php that will initialize the SCO. The value returned from the function is stored in a PHP variable called $initializeCache – I’ll talk about what it’s used for later.
Here’s my new initializeSCO() function in subs.php.
function initializeSCO() { global $link; global $SCOInstanceID; // has the SCO previously been initialized? $result = mysql_query("select count(VarName) from scormvars where (SCOInstanceID=$SCOInstanceID)",$link); list($count) = mysql_fetch_row($result); // not yet initialized - initialize all elements if (! $count) { // elements that tell the SCO which other elements are supported by this API initializeElement('cmi.core._children', 'student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,exit,session_time'); initializeElement('cmi.core.score._children','raw'); // student information initializeElement('cmi.core.student_name', getFromLMS('cmi.core.student_name')); initializeElement('cmi.core.student_id', getFromLMS('cmi.core.student_id')); // test score initializeElement('cmi.core.score.raw', ''); initializeElement('adlcp:masteryscore', getFromLMS('adlcp:masteryscore')); // SCO launch and suspend data initializeElement('cmi.launch_data', getFromLMS('cmi.launch_data')); initializeElement('cmi.suspend_data', ''); // progress and completion tracking initializeElement('cmi.core.lesson_location', ''); initializeElement('cmi.core.credit', 'credit'); initializeElement('cmi.core.lesson_status', 'not attempted'); initializeElement('cmi.core.entry', 'ab initio'); initializeElement('cmi.core.exit', ''); // seat time initializeElement('cmi.core.total_time', '0000:00:00'); initializeElement('cmi.core.session_time', ''); } // new session so clear pre-existing session time writeElement('cmi.core.session_time', ''); // create the javascript code that will be used to set up the javascript cache $initializeCache = "var cache = new Object();\n"; $result = mysql_query("select VarName, VarValue from scormvars where (SCOInstanceID=$SCOInstanceID)", $link); while (list($varname, $varvalue) = mysql_fetch_row($result)) { // make the value safe by escaping quotes and special characters $jvarvalue = addslashes($varvalue); // javascript to set the initial cache value $initializeCache .= "cache['$varname'] = '$jvarvalue';\n"; } // return javascript for cache initialization to the calling program return $initializeCache; }
Here’s how it breaks down – line-by-line:
- Lines 7 and 8 – I’m testing for whether this SCO has already been initialized in my LMS database table by counting the number of data elements corresponding to this SCOInstanceID.
- Lines 11 through 40 – These lines are only executed if the SCO hasn’t yet been initialized. They’re pretty much the same as were in the initialize.php code in Rev 1, and they set the initial database values for all of the SCORM data elements that I’m supporting.
- Line 43 – I clear any previous setting for the SCO session time data element.
- Lines 46 through 57 – Here, I’m going to create a set of JavaScript statements that will initialize the local cache. Now, obviously, I can’t do this using directly from the subs.php code since that’s being executed on the server. But, I’ll save them into a PHP variable and return them to the calling program where they can be executed on the client side.
So, when called, the initializeSCO() function sets up the initial database entries (if required) and then created the JavaScript initialization code. So, in api.php, I add the following.
<?php print $initializeCache; ?>
which, will result in the student’s browser seeing something like this:
var cache = new Object(); cache['cmi.core._children'] = 'student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,exit,session_time'; cache['cmi.core.score._children'] = 'raw'; cache['cmi.core.student_name'] = 'Addison, Steve'; cache['cmi.core.student_id'] = '007'; cache['cmi.core.score.raw'] = ''; cache['adlcp:masteryscore'] = '0'; cache['cmi.launch_data'] = ''; cache['cmi.suspend_data'] = ''; cache['cmi.core.lesson_location'] = ''; cache['cmi.core.credit'] = 'credit'; cache['cmi.core.lesson_status'] = 'not attempted'; cache['cmi.core.entry'] = 'ab initio'; cache['cmi.core.exit'] = ''; cache['cmi.core.total_time'] = '0000:00:00'; cache['cmi.core.session_time'] = '';
And, if you refer back to one of my earlier posts, you’ll see that this creates the local cache as a JavaScript object containing the initial values from the database.
Finally, I can get rid of the LMSInitialize() code, and modified form of api.php now looks like this.
<?php /* VS SCORM - JavaScript SCORM 1.2 RTE API Rev 4.0 - Friday, November 27, 2009 Copyright (C) 2009, Addison Robson LLC This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // essential functions require "subs.php"; // input data $SCOInstanceID = $_REQUEST['SCOInstanceID'] * 1; // read database login information and connect require "config.php"; dbConnect(); // initialize data elements in the database if they're not already set, and // dynamically create the javascript code to initialize the local cache $initializeCache = initializeSCO(); ?> <html> <head> <title></title> <script language="javascript"> // ------------------------------------------ // Status Flags // ------------------------------------------ var flagFinished = false; // ------------------------------------------ // SCO Data Cache - Initialization // ------------------------------------------ <?php print $initializeCache; ?> // ------------------------------------------ // SCORM RTE Functions - Initialization // ------------------------------------------ function LMSInitialize(dummyString) { return "true"; } // ------------------------------------------ // SCORM RTE Functions - Getting and Setting Values // ------------------------------------------ function LMSGetValue(varname) { return cache[varname]; } function LMSSetValue(varname,varvalue) { cache[varname] = varvalue; return "true"; } // ------------------------------------------ // SCORM RTE Functions - Saving the Cache to the Database // ------------------------------------------ function LMSCommit(dummyString) { // create request object var req = createRequest(); // code to prevent caching var d = new Date(); // set up request parameters - uses POST method req.open('POST','commit.php',false); // create a POST-formatted list of cached data elements // include only SCO-writeable data elements var params = 'SCOInstanceID=<?php print $SCOInstanceID; ?>&code='+d.getTime(); params += "&data[cmi.core.lesson_location]="+urlencode(cache['cmi.core.lesson_location']); params += "&data[cmi.core.lesson_status]="+urlencode(cache['cmi.core.lesson_status']); params += "&data[cmi.core.exit]="+urlencode(cache['cmi.core.exit']); params += "&data[cmi.core.session_time]="+urlencode(cache['cmi.core.session_time']); params += "&data[cmi.core.score.raw]="+urlencode(cache['cmi.core.score.raw']); params += "&data[cmi.suspend_data]="+urlencode(cache['cmi.suspend_data']); // request headers req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); req.setRequestHeader("Content-length", params.length); req.setRequestHeader("Connection", "close"); // submit to the server for processing req.send(params); // process returned data - error condition if (req.status != 200) { alert('Problem with AJAX Request in LMSCommit()'); return "false"; } // process returned data - OK else { return "true"; } } // ------------------------------------------ // SCORM RTE Functions - Closing The Session // ------------------------------------------ function LMSFinish(dummyString) { // already finished - prevent repeat call if (flagFinished) { return "true"; } // commit cached values to the database LMSCommit(''); // create request object var req = createRequest(); // code to prevent caching var d = new Date(); // set up request parameters - uses GET method req.open('GET','finish.php?SCOInstanceID=<?php print $SCOInstanceID; ?>&code='+d.getTime(),false); // submit to the server for processing req.send(null); // process returned data - error condition if (req.status != 200) { alert('Problem with AJAX Request in LMSFinish()'); return ""; } // set finish flag flagFinished = true; // return to calling program return "true"; } // ------------------------------------------ // SCORM RTE Functions - Error Handling // ------------------------------------------ function LMSGetLastError() { return 0; } function LMSGetDiagnostic(errorCode) { return "diagnostic string"; } function LMSGetErrorString(errorCode) { return "error string"; } // ------------------------------------------ // AJAX Request Handling // ------------------------------------------ function createRequest() { ... ... ... } // ------------------------------------------ // URL Encoding // ------------------------------------------ function urlencode( str ) { ... ... ... } </script> </head> <body> </body> </html>
Note that I’ve omitted the code for the createRequest() and urlencode() functions for clarity.
Next step – give it a trial run and see what happens.