Skip to content
Snippets Groups Projects
Commit 02d595e8 authored by srosse's avatar srosse
Browse files

Merge remote-tracking branch 'origin/OpenOLAT_15.2' into OpenOLAT_15.3

parents ddaf4c7f db1b32f6
No related branches found
No related tags found
No related merge requests found
......@@ -369,8 +369,8 @@ public class ScormRunController extends BasicController implements ScormAPICallb
@Override
public void lmsFinish(String olatSahsId, Properties scoreProp, Properties lessonStatusProp) {
doStartPage(null);
if (config.getBooleanSafe(ScormEditController.CONFIG_CLOSE_ON_FINISH, false)) {
doStartPage(null);
scormDispC.close();
}
}
......
......@@ -88,8 +88,8 @@ public class OLATApiAdapter implements ch.ethz.pfplms.scorm.api.ApiAdapterInterf
private Properties scoresProp; // keys: sahsId; values = raw score of an sco
private Properties lessonStatusProp;
private final String SCORE_IDENT = "cmi.core.score.raw";
private final String LESSON_STATUS_IDENT = "cmi.core.lesson_status";
private static final String SCORE_IDENT = "cmi.core.score.raw";
private static final String LESSON_STATUS_IDENT = "cmi.core.lesson_status";
private File scorePropsFile;
private File lessonStatusPropsFile;
......@@ -162,7 +162,7 @@ public class OLATApiAdapter implements ch.ethz.pfplms.scorm.api.ApiAdapterInterf
private final void say (String s) {
log.debug("core: "+s);
log.debug("core: {}", s);
}
......
......@@ -36,12 +36,16 @@ import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.olat.basesecurity.BaseSecurity;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.dispatcher.mapper.Mapper;
import org.olat.core.gui.media.MediaResource;
import org.olat.core.gui.media.ServletUtil;
import org.olat.core.gui.media.StringMediaResource;
import org.olat.core.id.Identity;
import org.olat.core.id.IdentityEnvironment;
......@@ -322,43 +326,90 @@ public class ScormAPIMapper implements Mapper, ScormAPICallback, Serializable {
String apiCallParamTwo = request.getParameter("apiCallParamTwo");
if(log.isDebugEnabled()) {
log.debug("scorm api request by user:"+ identity.getName() +": " + apiCall + "('" + apiCallParamOne + "' , '" + apiCallParamTwo + "')");
log.debug("scorm api request by user: {}: {} ('{}' , '{}')", identity.getName(), apiCall, apiCallParamOne, apiCallParamTwo);
}
StringMediaResource smr = new StringMediaResource();
smr.setContentType("text/html");
smr.setEncoding("utf-8");
if (apiCall != null && apiCall.equals("initcall")) {
//used for Mozilla / firefox only to get more time for fireing the onunload stuff triggered by overwriting the content.
smr.setData("<html><body></body></html>");
return smr;
log.info("Init call");
return createInitResource(request);
}
String returnValue = "";
if (apiCall != null) {
if (apiCall.equals(LMS_INITIALIZE)) {
returnValue = scormAdapter.LMSInitialize(apiCallParamOne);
} else if (apiCall.equals(LMS_GETVALUE)) {
returnValue = scormAdapter.LMSGetValue(apiCallParamOne);
} else if (apiCall.equals(LMS_SETVALUE)) {
returnValue = scormAdapter.LMSSetValue(apiCallParamOne, apiCallParamTwo);
} else if (apiCall.equals(LMS_COMMIT)) {
returnValue = scormAdapter.LMSCommit(apiCallParamOne);
} else if (apiCall.equals(LMS_FINISH)) {
returnValue = scormAdapter.LMSFinish(apiCallParamOne);
} else if (apiCall.equals(LMS_GETLASTERROR)) {
returnValue = scormAdapter.LMSGetLastError();
} else if (apiCall.equals(LMS_GETDIAGNOSTIC)) {
returnValue = scormAdapter.LMSGetDiagnostic(apiCallParamOne);
} else if (apiCall.equals(LMS_GETERRORSTRING)) {
returnValue = scormAdapter.LMSGetErrorString(apiCallParamOne);
String returnValue = apiCall(apiCall, apiCallParamOne, apiCallParamTwo);
return createResource(returnValue, request);
} else if(relPath.contains("batch")) {
try {
String batch = IOUtils.toString(request.getReader());
JSONArray batchArray = new JSONArray(batch);
for(int i=0; i<batchArray.length(); i++) {
JSONObject obj = batchArray.getJSONObject(i);
apiCall = obj.getString("apiCall");
apiCallParamOne = obj.getString("param1");
apiCallParamTwo = obj.getString("param2");
apiCall(apiCall, apiCallParamOne, apiCallParamTwo);
}
} catch (IOException e) {
log.error("", e);
}
smr.setData("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><p>"
+ returnValue + "</p></body></html>");
return smr;
}
smr.setData("");
return createResource("", request);
}
private String apiCall(String apiCall, String apiCallParamOne, String apiCallParamTwo) {
String returnValue = "";
if (apiCall.equals(LMS_INITIALIZE)) {
returnValue = scormAdapter.LMSInitialize(apiCallParamOne);
} else if (apiCall.equals(LMS_GETVALUE)) {
returnValue = scormAdapter.LMSGetValue(apiCallParamOne);
} else if (apiCall.equals(LMS_SETVALUE)) {
returnValue = scormAdapter.LMSSetValue(apiCallParamOne, apiCallParamTwo);
} else if (apiCall.equals(LMS_COMMIT)) {
returnValue = scormAdapter.LMSCommit(apiCallParamOne);
} else if (apiCall.equals(LMS_FINISH)) {
returnValue = scormAdapter.LMSFinish(apiCallParamOne);
} else if (apiCall.equals(LMS_GETLASTERROR)) {
returnValue = scormAdapter.LMSGetLastError();
} else if (apiCall.equals(LMS_GETDIAGNOSTIC)) {
returnValue = scormAdapter.LMSGetDiagnostic(apiCallParamOne);
} else if (apiCall.equals(LMS_GETERRORSTRING)) {
returnValue = scormAdapter.LMSGetErrorString(apiCallParamOne);
}
return returnValue;
}
private MediaResource createInitResource(HttpServletRequest request) {
MediaResource resource;
boolean acceptJson = ServletUtil.acceptJson(request);
if(acceptJson && request == null) {
resource = createHTMLResource("");
} else {
resource = createHTMLResource("<html><body></body></html>");
}
return resource;
}
private MediaResource createResource(String returnValue, HttpServletRequest request) {
MediaResource resource;
boolean acceptJson = ServletUtil.acceptJson(request);
if(acceptJson && request == null) {
resource = createHTMLResource("");
} else if(StringHelper.containsNonWhitespace(returnValue)) {
String data = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><p>" + returnValue + "</p></body></html>";
resource = createHTMLResource(data);
} else {
resource = createHTMLResource("");
}
return resource;
}
private StringMediaResource createHTMLResource(String data) {
StringMediaResource smr = new StringMediaResource();
smr.setContentType("text/html");
smr.setEncoding("utf-8");
smr.setData(data);
return smr;
}
}
\ No newline at end of file
......@@ -29,6 +29,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.olat.core.commons.fullWebApp.LayoutMain3ColsBackController;
import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
......@@ -73,7 +74,7 @@ import org.springframework.beans.factory.annotation.Autowired;
* the sco api calls to the scorm RTE backend. It provides also an navigation to
* navigate in the tree with "pre" "next" buttons.
*/
public class ScormAPIandDisplayController extends MainLayoutBasicController implements ConfigurationChangedListener {
public class ScormAPIandDisplayController extends MainLayoutBasicController implements ConfigurationChangedListener, ScormAPICallback {
protected static final String LMS_INITIALIZE = "LMSInitialize";
protected static final String LMS_GETVALUE = "LMSGetValue";
......@@ -136,6 +137,7 @@ public class ScormAPIandDisplayController extends MainLayoutBasicController impl
try {
scormAdapter = new OLATApiAdapter();
scormAdapter.addAPIListener(apiCallback);
scormAdapter.addAPIListener(this);
String fullname = UserManager.getInstance().getUserDisplayName(getIdentity());
String scormResourceIdStr = scormResourceId == null ? null : scormResourceId.toString();
scormAdapter.init(cpRoot, scormResourceIdStr, courseIdNodeId, FolderConfig.getCanonicalRoot(), username, fullname, lesson_mode, credit_mode, hashCode());
......@@ -322,6 +324,16 @@ public class ScormAPIandDisplayController extends MainLayoutBasicController impl
}
}
@Override
public void lmsCommit(String olatSahsId, Properties scoScores, Properties scoLessonStatus) {
//
}
@Override
public void lmsFinish(String olatSahsId, Properties scoProps, Properties scoLessonStatus) {
myContent.setDirty(false);
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(source == columnLayoutCtr) {
......
......@@ -10,7 +10,11 @@
## method to send a ping to the framework to draw itself after finish
function pingAfterFinish() {
window.suppressOlatOnUnloadOnce = true;
$r.javaScriptCommand('ping');
try {
$r.javaScriptCommand('ping');
} catch(e) {
if(window.console) console.log(e);
}
}
</script>
<script>
......
......@@ -16,137 +16,43 @@ var API = window;
* right now only used for moz, as ie has some problems with state handler
* room for improvement by makeing it into one function that works for both
*/
function scormApiRequest(remoteUrl)
{
this.remoteOLATurl = remoteUrl;
this.isMozilla = false;
this.httpReq=false;
this.reqCount = 0;
this.sendSyncSingle = asSendSyncSingle;
if (window.XMLHttpRequest)
{
this.httpReq = new XMLHttpRequest();
this.isMozilla = true;
if (debug) dump("func:scormApiRequest :is Mozilla\n");
}
// code for IE
else if (window.ActiveXObject)
{
try {
this.httpReq = new ActiveXObject("Msxml2.XMLHTTP");
if (debug) dump("func:scormApiRequest :is new IE \n");
} catch (e) {
try {
this.httpReq = new ActiveXObject("Microsoft.XMLHTTP");
if (debug) dump("func:scormApiRequest :is old IE \n");
} catch (E) {
this.httpReq = false;
}
}
}
}
function asSendSyncSingle(apiCall, param1, param2) {
// Sync - wait until data arrives
//IE does not like this function, what's it for?
//httpReq.multipart = false;
// Mozilla/Firefox bug 246518 workaround
// a new XMLHttpRequest object if needed
try {
this.httpReq.onload = showReq;
this.httpReq.onreadystatechange = showReq;
this.httpReq.open('POST', this.remoteOLATurl, false );
} catch (e) {
this.httpReq = new XMLHttpRequest();
this.httpReq.onload = showReq;
this.httpReq.onreadystatechange = showReq;
this.httpReq.open('POST', this.remoteOLATurl, false );
}
this.httpReq.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8');
this.httpReq.send('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
if(this.isMozilla)
{
if (debug) dump("func:asSencSingle: post successfull, calling showReq\n");
//add event listener geht auch! ev. besser?
//httpReq.addEventListener("load", showReq, false)
//this.httpReq.onload = showReq;
//this.httpReq.onreadystatechange = showReq;
} else {
if (debug) dump("func:asSencSingle: post successfull by this.httpReq, calling showReq\n");
//this.httpReq.onload = showReq;
//this.httpReq.onreadystatechange = showReq;
}
//httpReq.send(null);
}
function showReq( event ) {
if (debug) dump("func:showReq: inside func.: event type: " +event.type+"\n");
// ready state 4 means document loaded
//if ( event.target.readyState == 4 )
if ( this.readyState == 4 )
{
bMsg = document.getElementById( 'apiReturnHandler' );
//bMsg.innerHTML = event.target.responseText;
bMsg.innerHTML = this.responseText;
bMsg = null;
if (debug) dump("func:showReq: "+this.responseText+"\n");
} else { if (debug) dump( 'an error occured! Wrong readyState: ' + event.target.readyState +"\n"); }
function scormApiRequest(remoteUrl) {
this.remoteOLATurl = remoteUrl;
this.reqCount = 0;
}
/*************************************/
// global flag
var isIE = false;
// global request and XML document objects
var scormRTEresponse;
// retrieve XML document (reusable generic function);
function loadHTMLDoc(url,apiCall, param1, param2) {
var req;
// inner callback method to handle onreadystatechange event of req object
var processReqChange = function (event){
function loadHTMLDoc(url, async, apiCall, param1, param2) {
var scormResponse;
var req = new XMLHttpRequest();
req.onreadystatechange = function (event) {
// only if req shows "loaded"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
rteResponseText = req.responseText;
scormRTEresponse = rteResponseText.substring(rteResponseText.indexOf("<p>")+3,rteResponseText.indexOf("</p>"));
if (debug) dump(scormRTEresponse);
} else { if (debug) dump("There was a problem retrieving the XMLHttpRequest data:\n"+ req.statusText+"\n"); }
scormResponse = loadHTMLDocExtractResponse(req.responseText);
if (debug) dump(scormResponse);
} else if (debug) {
dump("There was a problem retrieving the XMLHttpRequest data:\n"+ req.statusText+"\n");
}
}
};
// branch for native XMLHttpRequest object
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
req.onreadystatechange = processReqChange;
//req.open("GET", url+'?apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ param2, false);
req.open("POST", url, false)
req.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8');
req.send('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
if (debug) dump('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
// branch for IE/Windows ActiveX version
} else if (window.ActiveXObject) {
isIE = true;
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange = processReqChange;
//req.open("GET", url+'?apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ param2 + '&rnd='+increment(), false);
//req.send();
req.open("POST", url, false);
req.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8');
req.send('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
}
}
// Help GC
req = null;
req.open("POST", url, async);
req.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8');
req.send('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
if (debug) dump('apiCall='+ apiCall + '&apiCallParamOne='+ param1 + '&apiCallParamTwo='+ encodeURIComponent(param2));
return scormResponse;
}
function loadHTMLDocExtractResponse(responseText) {
if("<html><body></body></html>" == responseText) {
return "";// initial call
}
return responseText.substring(responseText.indexOf("<p>") + 3, responseText.indexOf("</p>"));
}
/*****************************************************************
......@@ -164,20 +70,15 @@ function increment(){
* over a synchronous XmlHttpRequest.
* Code uses different ways for moz and ie.
*******************************************************************/
function passApiCall(apiCall, param1, param2){
if(window.ActiveXObject || (navigator.userAgent.indexOf("Safari") != -1)){
loadHTMLDoc(olatCommandUri,apiCall,param1,param2);
return scormRTEresponse;
}else if(window.XMLHttpRequest && (navigator.userAgent.indexOf("Mozilla") != -1)){
jsHttpRequest.sendSyncSingle(apiCall,param1, param2);
var responseHTML = document.getElementById( 'apiReturnHandler' ).innerHTML;
return responseHTML.substring(responseHTML.indexOf("<p>")+3,responseHTML.indexOf("</p>"));
}else{
if (debug) dump("Browser does not support the needed XmlHttpRequest\n");
function passApiCall(apiCall, param1, param2) {
try {
return loadHTMLDoc(olatCommandUri, false, apiCall, param1, param2);
} catch(e) {
if(window.console) console.log(e);
}
}
/**
* triggers the onunload stuff often used in scorm content to finish an sco by
* opening the iframe document for writing (IE) or replacing the content doc (Mozilla).
......@@ -185,34 +86,27 @@ function passApiCall(apiCall, param1, param2){
function olatonunload(){
if (debug) dump("func:olatonunload: is called\n");
if(window.frameId && document.getElementById(frameId) && this.frames[frameId]){
if(window.ActiveXObject){
//on IE by opening the document, the onunload event gets triggered
var iframeDoc = this.frames[frameId].document;
iframeDoc.open();
iframeDoc.write("<html><body></body></html>");
iframeDoc.close();
iframeDoc = null;
} else {
// Mozilla and others
var iframeDoc = document.getElementById(frameId).contentDocument;
iframeDoc.location.replace("about:blank");
iframeDoc = null;
//delay(200);
//if((new Date().getTime() - lastRequest) > 60000) alert("Current SCO will be finished before initializing next SCO or terminating! Press OK to proceed.");
var delayReq = new XMLHttpRequest();
//"false" waits until the result arrived;
delayReq.open('GET', olatCommandUri, false );
delayReq.send(null);
}
// Mozilla and others
var iframeDoc = document.getElementById(frameId).contentDocument;
iframeDoc.location.replace("about:blank");
iframeDoc = null;
var delayReq = new XMLHttpRequest();
//"false" waits until the result arrived;
delayReq.open('GET', olatCommandUri, false );
delayReq.send(null);
}
return true;
}
function delay(gap){
var then,now; then=new Date().getTime();
now=then;
while((now-then)<gap)
{now=new Date().getTime();}
var openolatScormUnloadQueue = new Array();
function queueCall(apiCall, param1, param2) {
var entry = new Object();
entry.apiCall = apiCall;
entry.param1 = param1;
entry.param2 = param2;
openolatScormUnloadQueue.push(entry);
}
/******************************************************************
......@@ -224,7 +118,14 @@ function LMSInitialize (s) {
return passApiCall('LMSInitialize',s,'');
}
function LMSFinish (s) {
var finishedResult = passApiCall('LMSFinish',s,'');
var finishedResult = passApiCall('LMSFinish', s, '');
if(typeof val === "undefined") {
// Communication problem, try to send all queued data as beacon
queueCall('LMSFinish', s, '');
var data = JSON.stringify(openolatScormUnloadQueue);
openolatScormUnloadQueue = new Array();
navigator.sendBeacon(olatCommandUri + "/batch/data/", data);
}
// Immediately close module, ping OpenOlat main window to take over control
setTimeout(function(){
try {
......@@ -236,20 +137,38 @@ function LMSFinish (s) {
return finishedResult;
}
function LMSSetValue (l, r) {
return passApiCall('LMSSetValue',l,r);
var val = passApiCall('LMSSetValue',l,r);
if(typeof val === "undefined") {
queueCall('LMSSetValue', l, r);
val = r;
}
return val;
}
function LMSGetValue (s) {
return passApiCall('LMSGetValue',s,'');
}
function LMSGetLastError () {
return passApiCall('LMSGetLastError','','');
var val = passApiCall('LMSGetLastError','','');
if(typeof val === "undefined") {
val = "0";
}
return val;
}
function LMSGetErrorString (s) {
return passApiCall('LMSGetErrorString',s,'');
var val = passApiCall('LMSGetErrorString',s,'');
if(typeof val === "undefined") {
val = "No Error";
}
return val;
}
function LMSGetDiagnostic (s) {
return passApiCall('LMSGetDiagnostic',s,'');
}
function LMSCommit (s) {
return passApiCall('LMSCommit',s,'');
var val = passApiCall('LMSCommit',s,'');
if(typeof val === "undefined") {
queueCall('LMSCommit', s, '');
val = r;
}
return val;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment