MDL-67175 mod_scorm: sync XHR requests use sendBeacon when available

Totara reference TL-22621 (original code by Sam Hemelryk)
2add402f81
This commit is contained in:
Sam Hemelryk 2019-10-15 17:28:25 +13:00 committed by Andrew Nicols
parent 86b082cece
commit 841d855993
6 changed files with 135 additions and 5 deletions

View File

@ -534,6 +534,11 @@ function AICCapi(def, cmiobj, scormauto, cfgwwwroot, scormid, scoid, attempt, vi
//popupwin(datastring);
var myRequest = NewHttpReq();
result = DoRequest(myRequest,datamodelurl,datamodelurlparams + datastring);
if (result === false) {
return false;
}
results = String(result).split('\n');
errorCode = results[1];
return results[0];

View File

@ -648,6 +648,11 @@ function SCORMapi1_2(def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebu
var myRequest = NewHttpReq();
//alert('going to:' + "<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php" + "id=<?php p($id) ?>&a=<?php p($a) ?>&sesskey=<?php echo sesskey() ?>"+datastring);
result = DoRequest(myRequest,datamodelurl,datamodelurlparams + datastring);
if (result === false) {
return false;
}
results = String(result).split('\n');
errorCode = results[1];
return results[0];

View File

@ -1218,6 +1218,10 @@ function SCORMapi1_3(def, cmiobj, cmiint, cmicommentsuser, cmicommentslms, scorm
var myRequest = NewHttpReq();
result = DoRequest(myRequest, datamodelurl, datamodelurlparams + datastring);
if (result === false) {
return false;
}
var results = String(result).split('\n');
if ((results.length > 2) && (navrequest != '')) {
eval(results[2]);

View File

@ -460,9 +460,17 @@ M.mod_scorm.init = function(Y, nav_display, navposition_left, navposition_top, h
if (scoes_nav[launch_sco].flow === 1) {
var datastring = scoes_nav[launch_sco].url + '&function=scorm_seq_flow&request=backward';
result = scorm_ajax_request(M.cfg.wwwroot + '/mod/scorm/datamodels/sequencinghandler.php?', datastring);
mod_scorm_seq = encodeURIComponent(result);
result = Y.JSON.parse (result);
if (typeof result.nextactivity.id != undefined) {
if (result === false) {
// Either the outcome was a failure, or we are unloading and simply just don't know
// what the outcome actually was.
result = {};
} else {
mod_scorm_seq = encodeURIComponent(result);
result = Y.JSON.parse(result);
}
if (typeof result.nextactivity !== 'undefined' && typeof result.nextactivity.id !== 'undefined') {
var node = scorm_prev(scorm_tree_node.getSelectedNodes()[0]);
if (node == null) {
// Avoid use of TreeView for Navigation.
@ -492,8 +500,16 @@ M.mod_scorm.init = function(Y, nav_display, navposition_left, navposition_top, h
if (scoes_nav[launch_sco].flow === 1) {
var datastring = scoes_nav[launch_sco].url + '&function=scorm_seq_flow&request=forward';
result = scorm_ajax_request(M.cfg.wwwroot + '/mod/scorm/datamodels/sequencinghandler.php?', datastring);
mod_scorm_seq = encodeURIComponent(result);
result = Y.JSON.parse (result);
if (result === false) {
// Either the outcome was a failure, or we are unloading and simply just don't know
// what the outcome actually was.
result = {};
} else {
mod_scorm_seq = encodeURIComponent(result);
result = Y.JSON.parse(result);
}
if (typeof result.nextactivity !== 'undefined' && typeof result.nextactivity.id !== 'undefined') {
var node = scorm_next(scorm_tree_node.getSelectedNodes()[0]);
if (node === null) {

View File

@ -37,6 +37,34 @@ function NewHttpReq() {
function DoRequest(httpReq,url,param) {
// If we are unloading, and we can use sendBeacon then do that, Chrome does not permit synchronous XHR requests on unload.
if (window.mod_scorm_is_window_closing && navigator && navigator.sendBeacon && FormData) {
// Ok, old API alert, the param is a URI encoded string. We need to split it and convert it to a supported format.
// I've chosen FormData and FormData.append as they are compatible with our supported browsers:
// - https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData
// - https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
var vars = param.split('&'),
i = 0,
pair,
key,
value,
formData = new FormData();
for (i = 0; i < vars.length; i++) {
pair = vars[i].split('=');
key = decodeURIComponent(pair[0]);
value = decodeURIComponent(pair[1]);
formData.append(key, value);
}
// We'll also inform it that we are unloading, potentially useful in the future.
formData.append('unloading', '1');
// The results is true or false, we don't get the response from the server. Make it look like it was a success.
navigator.sendBeacon(url, formData);
// This is what a success looks like when it comes back from the server.
return "true\n0";
}
// httpReq.open (Method("get","post"), URL(string), Asyncronous(true,false))
//popupwin(url+"\n"+param);
httpReq.open("POST", url,false);
@ -60,3 +88,24 @@ function popupwin(content) {
op.document.write(content);
op.document.close();
}
/**
* We wire up a small marker for the unload events triggered when the user is navigating away or closing the tab.
* This is done because Chrome does not allow synchronous XHR requests on the following unload events:
* - beforeunload
* - unload
* - pagehide
* - visibilitychange
*/
(function() {
// Set up a global var. Sorry about this, old code ... old ways.
window.mod_scorm_is_window_closing = false;
var toggle = function() {
window.mod_scorm_is_window_closing = true;
};
// Listen to the four events known to represent an unload operation.
window.addEventListener('beforeunload', toggle);
window.addEventListener('unload', toggle);
window.addEventListener('pagehide', toggle);
window.addEventListener('visibilitychange', toggle);
})();

View File

@ -0,0 +1,51 @@
@mod @mod_scorm @_file_upload @_switch_iframe @_alert
Feature: Confirm progress gets saved on unload events
In order to let students access a scorm package
As a teacher
I need to add scorm activity to a course
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
@javascript
Scenario: Test progress gets saved correctly when the user navigates away from the scorm activity
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "SCORM package" to section "1"
And I set the following fields to these values:
| Name | Runtime Basic Calls SCORM 2004 3rd Edition package |
| Description | Description |
And I upload "mod/scorm/tests/packages/RuntimeBasicCalls_SCORM20043rdEdition.zip" file to "Package file" filemanager
And I click on "Save and display" "button"
And I should see "Runtime Basic Calls SCORM 2004 3rd Edition package"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Runtime Basic Calls SCORM 2004 3rd Edition package"
Then I should see "Normal"
And I press "Enter"
And I switch to "scorm_object" iframe
And I press "Next"
And I press "Next"
And I switch to "contentFrame" iframe
And I should see "Scoring"
And I switch to the main frame
And I follow "C1"
And I follow "Runtime Basic Calls SCORM 2004 3rd Edition package"
And I should see "Normal"
And I click on "Enter" "button" confirming the dialogue
And I switch to "scorm_object" iframe
And I switch to "contentFrame" iframe
And I should see "Scoring"
And I switch to the main frame
# Go away from the scorm to stop background requests
And I am on homepage