first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+561
View File
@@ -0,0 +1,561 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
//
// SCORM 1.2 API Implementation
//
function AICCapi(def, cmiobj, scormauto, cfgwwwroot, scormid, scoid, attempt, viewmode, currentorg, sesskey, cmid, autocommit) {
var prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
var datamodelurl = cfgwwwroot + "/mod/scorm/datamodel.php";
var datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
// Standard Data Type Definition
CMIString256 = '^.{0,255}$';
CMIString4096 = '^.{0,4096}$';
CMITime = '^([0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\.[0-9]{1,2})?$';
CMITimespan = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$';
CMIInteger = '^\\d+$';
CMISInteger = '^-?([0-9]+)$';
CMIDecimal = '^-?([0-9]{0,3})(\.[0-9]{1,2})?$';
CMIIdentifier = '^\\w{1,255}$';
CMIFeedback = CMIString256; // This must be redefined
CMIIndex = '[._](\\d+).';
// Vocabulary Data Type Definition
CMIStatus = '^passed$|^completed$|^failed$|^incomplete$|^browsed$';
CMIStatus2 = '^passed$|^completed$|^failed$|^incomplete$|^browsed$|^not attempted$';
CMIExit = '^time-out$|^suspend$|^logout$|^$';
CMIType = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$';
CMIResult = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\.[0-9]{1,2})?$';
NAVEvent = '^previous$|^continue$';
// Children lists
cmi_children = 'core, suspend_data, launch_data, comments, objectives, student_data, student_preference, interactions';
core_children = 'student_id, student_name, lesson_location, credit, lesson_status, entry, score, total_time, lesson_mode, exit, session_time';
score_children = 'raw, min, max';
comments_children = 'content, location, time';
objectives_children = 'id, score, status';
student_data_children = 'attempt_number, tries, mastery_score, max_time_allowed, time_limit_action';
student_preference_children = 'audio, language, speed, text';
interactions_children = 'id, objectives, time, type, correct_responses, weighting, student_response, result, latency';
// Data ranges
score_range = '0#100';
audio_range = '-1#100';
speed_range = '-100#100';
weighting_range = '-100#100';
text_range = '-1#1';
// The AICC data model
var datamodel = {};
for(scoid in def){
datamodel[scoid] = {
'cmi._children':{'defaultvalue':cmi_children, 'mod':'r', 'writeerror':'402'},
'cmi._version':{'defaultvalue':'3.4', 'mod':'r', 'writeerror':'402'},
'cmi.core._children':{'defaultvalue':core_children, 'mod':'r', 'writeerror':'402'},
'cmi.core.student_id':{'defaultvalue':def[scoid]['cmi.core.student_id'], 'mod':'r', 'writeerror':'403'},
'cmi.core.student_name':{'defaultvalue':def[scoid]['cmi.core.student_name'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_location':{'defaultvalue':def[scoid]['cmi.core.lesson_location'], 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.core.credit':{'defaultvalue':def[scoid]['cmi.core.credit'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_status':{'defaultvalue':def[scoid]['cmi.core.lesson_status'], 'format':CMIStatus, 'mod':'rw', 'writeerror':'405'},
'cmi.core.exit':{'defaultvalue':def[scoid]['cmi.core.exit'], 'format':CMIExit, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.core.entry':{'defaultvalue':def[scoid]['cmi.core.entry'], 'mod':'r', 'writeerror':'403'},
'cmi.core.score._children':{'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'},
'cmi.core.score.raw':{'defaultvalue':def[scoid]['cmi.core.score.raw'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.score.max':{'defaultvalue':def[scoid]['cmi.core.score.max'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.score.min':{'defaultvalue':def[scoid]['cmi.core.score.min'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'00:00:00', 'readerror':'404', 'writeerror':'405'},
'cmi.core.total_time':{'defaultvalue':def[scoid]['cmi.core.total_time'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_mode':{'defaultvalue':def[scoid]['cmi.core.lesson_mode'], 'mod':'r', 'writeerror':'403'},
'cmi.suspend_data':{'defaultvalue':def[scoid]['cmi.suspend_data'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
'cmi.launch_data':{'defaultvalue':def[scoid]['cmi.launch_data'], 'mod':'r', 'writeerror':'403'},
'cmi.comments':{'defaultvalue':def[scoid]['cmi.comments'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
// deprecated evaluation attributes
'cmi.evaluation.comments._count':{'defaultvalue':'0', 'mod':'r', 'writeerror':'402'},
'cmi.evaluation.comments._children':{'defaultvalue':comments_children, 'mod':'r', 'writeerror':'402'},
'cmi.evaluation.comments.n.content':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.evaluation.comments.n.location':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.evaluation.comments.n.time':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMITime, 'mod':'rw', 'writeerror':'405'},
'cmi.comments_from_lms':{'mod':'r', 'writeerror':'403'},
'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r', 'writeerror':'402'},
'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'mod':'r', 'writeerror':'402'},
'cmi.objectives.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.status':{'pattern':CMIIndex, 'format':CMIStatus2, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data._children':{'defaultvalue':student_data_children, 'mod':'r', 'writeerror':'402'},
'cmi.student_data.attempt_number':{'defaultvalue':def[scoid]['cmi.student_data.attempt_number'], 'mod':'r', 'writeerror':'402'},
'cmi.student_data.tries.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data.tries.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data.tries.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data.tries.n.status':{'pattern':CMIIndex, 'format':CMIStatus2, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data.tries.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data.mastery_score':{'defaultvalue':def[scoid]['cmi.student_data.mastery_score'], 'mod':'r', 'writeerror':'403'},
'cmi.student_data.max_time_allowed':{'defaultvalue':def[scoid]['cmi.student_data.max_time_allowed'], 'mod':'r', 'writeerror':'403'},
'cmi.student_data.time_limit_action':{'defaultvalue':def[scoid]['cmi.student_data.time_limit_action'], 'mod':'r', 'writeerror':'403'},
'cmi.student_data.tries_during_lesson':{'defaultvalue':def[scoid]['cmi.student_data.tries_during_lesson'], 'mod':'r', 'writeerror':'402'},
'cmi.student_preference._children':{'defaultvalue':student_preference_children, 'mod':'r', 'writeerror':'402'},
'cmi.student_preference.audio':{'defaultvalue':'0', 'format':CMISInteger, 'range':audio_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.language':{'defaultvalue':'', 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.speed':{'defaultvalue':'0', 'format':CMISInteger, 'range':speed_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.text':{'defaultvalue':'0', 'format':CMISInteger, 'range':text_range, 'mod':'rw', 'writeerror':'405'},
'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r', 'writeerror':'402'},
'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.correct_responses._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':weighting_range, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.student_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'nav.event':{'defaultvalue':'', 'format':NAVEvent, 'mod':'w', 'readerror':'404', 'writeerror':'405'}
};
}
var cmi, nav;
function initdatamodel(scoid){
prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
//
// Datamodel inizialization
//
cmi = new Object();
cmi.core = new Object();
cmi.core.score = new Object();
cmi.objectives = new Object();
cmi.student_data = new Object();
cmi.student_preference = new Object();
cmi.interactions = new Object();
// deprecated evaluation attributes
cmi.evaluation = new Object();
cmi.evaluation.comments = new Object();
// Navigation Object
nav = new Object();
for (element in datamodel[scoid]) {
if (element.match(/\.n\./) == null) {
if (typeof datamodel[scoid][element].defaultvalue != 'undefined') {
eval(element + ' = datamodel["' + scoid + '"]["' + element + '"].defaultvalue;');
} else {
eval(element + ' = "";');
}
}
}
eval(cmiobj[scoid]);
if (cmi.core.lesson_status == '') {
cmi.core.lesson_status = 'not attempted';
}
}
//
// API Methods definition
//
var Initialized = false;
function LMSInitialize (param) {
scoid = scorm_current_node ? scorm_current_node.scoid : scoid;
initdatamodel(scoid);
errorCode = "0";
if (param == "") {
if (!Initialized) {
Initialized = true;
errorCode = "0";
return "true";
} else {
errorCode = "101";
}
} else {
errorCode = "201";
}
return "false";
}
function LMSFinish (param) {
errorCode = "0";
if (param == "") {
if (Initialized) {
Initialized = false;
result = StoreData(cmi,true);
if (nav.event != '') {
if (nav.event == 'continue') {
setTimeout('mod_scorm_launch_next_sco();',500);
} else {
setTimeout('mod_scorm_launch_prev_sco();',500);
}
} else {
if (scormauto == 1) {
setTimeout('mod_scorm_launch_next_sco();',500);
}
}
// trigger TOC update
var callback = M.mod_scorm.connectPrereqCallback;
YUI().use('io-base', function(Y) {
Y.on('io:complete', callback.success, Y);
Y.io(prerequrl);
});
return "true";
} else {
errorCode = "301";
}
} else {
errorCode = "201";
}
return "false";
}
function LMSGetValue (element) {
errorCode = "0";
if (Initialized) {
if (element != "") {
expression = new RegExp(CMIIndex,'g');
elementmodel = String(element).replace(expression,'.n.');
if (typeof datamodel[scoid][elementmodel] != "undefined") {
if (datamodel[scoid][elementmodel].mod != 'w') {
element = String(element).replace(expression, "_$1.");
elementIndexes = element.split('.');
subelement = 'cmi';
i = 1;
while ((i < elementIndexes.length) && (typeof eval(subelement) != "undefined")) {
subelement += '.' + elementIndexes[i++];
}
if (subelement == element) {
errorCode = "0";
return eval(element);
} else {
errorCode = "0"; // Need to check if it is the right errorCode
}
} else {
errorCode = datamodel[scoid][elementmodel].readerror;
}
} else {
childrenstr = '._children';
countstr = '._count';
if (elementmodel.substr(elementmodel.length - childrenstr.length,elementmodel.length) == childrenstr) {
parentmodel = elementmodel.substr(0,elementmodel.length - childrenstr.length);
if (typeof datamodel[scoid][parentmodel] != "undefined") {
errorCode = "202";
} else {
errorCode = "201";
}
} else if (elementmodel.substr(elementmodel.length - countstr.length,elementmodel.length) == countstr) {
parentmodel = elementmodel.substr(0,elementmodel.length - countstr.length);
if (typeof datamodel[scoid][parentmodel] != "undefined") {
errorCode = "203";
} else {
errorCode = "201";
}
} else {
errorCode = "201";
}
}
} else {
errorCode = "201";
}
} else {
errorCode = "301";
}
return "";
}
function LMSSetValue (element,value) {
errorCode = "0";
if (Initialized) {
if (element != "") {
expression = new RegExp(CMIIndex,'g');
elementmodel = String(element).replace(expression,'.n.');
if (typeof datamodel[scoid][elementmodel] != "undefined") {
if (datamodel[scoid][elementmodel].mod != 'r') {
expression = new RegExp(datamodel[scoid][elementmodel].format);
value = value + '';
matches = value.match(expression);
if (matches != null) {
//Create dynamic data model element
if (element != elementmodel) {
elementIndexes = element.split('.');
subelement = 'cmi';
for (i = 1; i < elementIndexes.length - 1; i++) {
elementIndex = elementIndexes[i];
if (elementIndexes[i + 1].match(/^\d+$/)) {
if ((typeof eval(subelement + '.' + elementIndex)) == "undefined") {
eval(subelement + '.' + elementIndex + ' = new Object();');
eval(subelement + '.' + elementIndex + '._count = 0;');
}
if (elementIndexes[i + 1] == eval(subelement + '.' + elementIndex + '._count')) {
eval(subelement + '.' + elementIndex + '._count++;');
}
if (elementIndexes[i + 1] > eval(subelement + '.' + elementIndex + '._count')) {
errorCode = "201";
}
subelement = subelement.concat('.' + elementIndex + '_' + elementIndexes[i + 1]);
i++;
} else {
subelement = subelement.concat('.' + elementIndex);
}
if ((typeof eval(subelement)) == "undefined") {
eval(subelement + ' = new Object();');
if (subelement.substr(0,14) == 'cmi.objectives') {
eval(subelement + '.score = new Object();');
eval(subelement + '.score._children = score_children;');
eval(subelement + '.score.raw = "";');
eval(subelement + '.score.min = "";');
eval(subelement + '.score.max = "";');
}
if (subelement.substr(0,16) == 'cmi.interactions') {
eval(subelement + '.objectives = new Object();');
eval(subelement + '.objectives._count = 0;');
eval(subelement + '.correct_responses = new Object();');
eval(subelement + '.correct_responses._count = 0;');
}
}
}
element = subelement.concat('.' + elementIndexes[elementIndexes.length - 1]);
}
// Store data.
if (errorCode == "0") {
if (autocommit && !(AICCapi.timeout)) {
AICCapi.timeout = Y.later(60000, API, 'LMSCommit', [""], false);
}
if (typeof datamodel[scoid][elementmodel].range != "undefined") {
range = datamodel[scoid][elementmodel].range;
ranges = range.split('#');
value = value * 1.0;
if ((value >= ranges[0]) && (value <= ranges[1])) {
eval(element + '="' + value + '";');
errorCode = "0";
return "true";
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
if (element == 'cmi.comments') {
eval(element + '+="' + value + '";');
} else {
eval(element + '="' + value + '";');
}
errorCode = "0";
return "true";
}
}
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
errorCode = "201"
}
} else {
errorCode = "201";
}
} else {
errorCode = "301";
}
return "false";
}
function LMSCommit (param) {
if (AICCapi.timeout) {
AICCapi.timeout.cancel();
AICCapi.timeout = null;
}
errorCode = "0";
if (param == "") {
if (Initialized) {
result = StoreData(cmi,false);
return "true";
} else {
errorCode = "301";
}
} else {
errorCode = "201";
}
return "false";
}
function LMSGetLastError () {
return errorCode;
}
function LMSGetErrorString (param) {
if (param != "") {
var errorString = new Array();
errorString["0"] = "No error";
errorString["101"] = "General exception";
errorString["201"] = "Invalid argument error";
errorString["202"] = "Element cannot have children";
errorString["203"] = "Element not an array - cannot have count";
errorString["301"] = "Not initialized";
errorString["401"] = "Not implemented error";
errorString["402"] = "Invalid set value, element is a keyword";
errorString["403"] = "Element is read only";
errorString["404"] = "Element is write only";
errorString["405"] = "Incorrect data type";
return errorString[param];
} else {
return "";
}
}
function LMSGetDiagnostic (param) {
if (param == "") {
param = errorCode;
}
return param;
}
function AddTime (first, second) {
var sFirst = first.split(":");
var sSecond = second.split(":");
var cFirst = sFirst[2].split(".");
var cSecond = sSecond[2].split(".");
var change = 0;
FirstCents = 0; //Cents
if (cFirst.length > 1) {
FirstCents = parseInt(cFirst[1],10);
}
SecondCents = 0;
if (cSecond.length > 1) {
SecondCents = parseInt(cSecond[1],10);
}
var cents = FirstCents + SecondCents;
change = Math.floor(cents / 100);
cents = cents - (change * 100);
if (Math.floor(cents) < 10) {
cents = "0" + cents.toString();
}
var secs = parseInt(cFirst[0],10) + parseInt(cSecond[0],10) + change; //Seconds
change = Math.floor(secs / 60);
secs = secs - (change * 60);
if (Math.floor(secs) < 10) {
secs = "0" + secs.toString();
}
mins = parseInt(sFirst[1],10) + parseInt(sSecond[1],10) + change; //Minutes
change = Math.floor(mins / 60);
mins = mins - (change * 60);
if (mins < 10) {
mins = "0" + mins.toString();
}
hours = parseInt(sFirst[0],10) + parseInt(sSecond[0],10) + change; //Hours
if (hours < 10) {
hours = "0" + hours.toString();
}
if (cents != '0') {
return hours + ":" + mins + ":" + secs + '.' + cents;
} else {
return hours + ":" + mins + ":" + secs;
}
}
function TotalTime() {
total_time = AddTime(cmi.core.total_time, cmi.core.session_time);
return '&' + underscore('cmi.core.total_time') + '=' + escape(total_time);
}
function CollectData(data,parent) {
var datastring = '';
for (property in data) {
if (typeof data[property] == 'object') {
datastring += CollectData(data[property],parent + '.' + property);
} else {
element = parent + '.' + property;
expression = new RegExp(CMIIndex,'g');
elementmodel = String(element).replace(expression,'.n.');
if (typeof datamodel[scoid][elementmodel] != "undefined") {
if (datamodel[scoid][elementmodel].mod != 'r') {
elementstring = '&' + underscore(element) + '=' + escape(data[property]);
if (typeof datamodel[scoid][elementmodel].defaultvalue != "undefined") {
if (datamodel[scoid][elementmodel].defaultvalue != data[property]) {
datastring += elementstring;
}
} else {
datastring += elementstring;
}
}
}
}
}
return datastring;
}
function StoreData(data,storetotaltime) {
var datastring = '';
if (storetotaltime) {
if (cmi.core.lesson_mode == 'normal') {
if (cmi.core.credit == 'credit') {
if (cmi.student_data.mastery_score != '' && cmi.core.score.raw != '') {
if (cmi.core.score.raw >= cmi.student_data.mastery_score) {
cmi.core.lesson_status = 'passed';
} else {
cmi.core.lesson_status = 'failed';
}
}
}
}
if (cmi.core.lesson_mode == 'browse') {
if (datamodel[scoid]['cmi.core.lesson_status'].defaultvalue == '' && cmi.core.lesson_status == 'not attempted') {
cmi.core.lesson_status = 'browsed';
}
}
datastring = CollectData(data,'cmi');
datastring += TotalTime();
} else {
datastring = CollectData(data,'cmi');
}
var myRequest = NewHttpReq();
var result = DoRequest(myRequest, datamodelurl, datamodelurlparams + datastring);
if (result === false) {
return false;
}
results = String(result).split('\n');
errorCode = results[1];
return results[0];
}
this.LMSInitialize = LMSInitialize;
this.LMSFinish = LMSFinish;
this.LMSGetValue = LMSGetValue;
this.LMSSetValue = LMSSetValue;
this.LMSCommit = LMSCommit;
this.LMSGetLastError = LMSGetLastError;
this.LMSGetErrorString = LMSGetErrorString;
this.LMSGetDiagnostic = LMSGetDiagnostic;
}
M.scorm_api = {};
M.scorm_api.init = function(Y, def, cmiobj, scormauto, cfgwwwroot, scormid, scoid, attempt, viewmode, currentorg, sesskey, cmid, autocommit) {
window.API = new AICCapi(def, cmiobj, scormauto, cfgwwwroot, scormid, scoid, attempt, viewmode, currentorg, sesskey, cmid, autocommit);
}
+66
View File
@@ -0,0 +1,66 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
require_once($CFG->dirroot.'/mod/scorm/locallib.php');
$userdata = new stdClass();
$def = new stdClass();
$cmiobj = new stdClass();
if (!isset($currentorg)) {
$currentorg = '';
}
if ($scoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'sortorder, id')) {
// Drop keys so that it is a simple array.
$scoes = array_values($scoes);
foreach ($scoes as $sco) {
$def->{($sco->id)} = new stdClass();
$userdata->{($sco->id)} = new stdClass();
$def->{($sco->id)} = get_scorm_default($userdata->{($sco->id)}, $scorm, $sco->id, $attempt, $mode);
// Reconstitute objectives, comments_from_learner and comments_from_lms.
$cmiobj->{($sco->id)} = '';
$currentobj = '';
$count = 0;
foreach ($userdata as $element => $value) {
if (substr($element, 0, 14) == 'cmi.objectives') {
$element = preg_replace('/\.(\d+)\./', "_\$1.", $element);
preg_match('/\_(\d+)\./', $element, $matches);
if (count($matches) > 0 && $currentobj != $matches[1]) {
$currentobj = $matches[1];
$count++;
$end = strpos($element, $matches[1]) + strlen($matches[1]);
$subelement = substr($element, 0, $end);
$cmiobj->{($sco->id)} .= ' '.$subelement." = new Object();\n";
$cmiobj->{($sco->id)} .= ' '.$subelement.".score = new Object();\n";
$cmiobj->{($sco->id)} .= ' '.$subelement.".score._children = score_children;\n";
$cmiobj->{($sco->id)} .= ' '.$subelement.".score.raw = '';\n";
$cmiobj->{($sco->id)} .= ' '.$subelement.".score.min = '';\n";
$cmiobj->{($sco->id)} .= ' '.$subelement.".score.max = '';\n";
}
$cmiobj->{($sco->id)} .= ' '.$element.' = \''.$value."';\n";
}
}
if ($count > 0) {
$cmiobj->{($sco->id)} .= ' cmi.objectives._count = '.$count.";\n";
}
}
}
$PAGE->requires->js_init_call('M.scorm_api.init', array($def, $cmiobj, $scorm->auto, $CFG->wwwroot, $scorm->id, $scoid,
$attempt, $mode, $currentorg, sesskey(), $id, $scorm->autocommit));
+575
View File
@@ -0,0 +1,575 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* functions used by AICC packages.
*
* @package mod_scorm
* @copyright 1999 onwards Roberto Pinna
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function scorm_add_time($a, $b) {
$aes = explode(':', $a);
$bes = explode(':', $b);
$aseconds = explode('.', $aes[2]);
$bseconds = explode('.', $bes[2]);
$change = 0;
$acents = 0; // Cents.
if (count($aseconds) > 1) {
$acents = $aseconds[1];
}
$bcents = 0;
if (count($bseconds) > 1) {
$bcents = $bseconds[1];
}
$cents = $acents + $bcents;
$change = floor($cents / 100);
$cents = $cents - ($change * 100);
if (floor($cents) < 10) {
$cents = '0'. $cents;
}
$secs = $aseconds[0] + $bseconds[0] + $change; // Seconds.
$change = floor($secs / 60);
$secs = $secs - ($change * 60);
if (floor($secs) < 10) {
$secs = '0'. $secs;
}
$mins = $aes[1] + $bes[1] + $change; // Minutes.
$change = floor($mins / 60);
$mins = $mins - ($change * 60);
if ($mins < 10) {
$mins = '0' . $mins;
}
$hours = $aes[0] + $bes[0] + $change; // Hours.
if ($hours < 10) {
$hours = '0' . $hours;
}
if ($cents != '0') {
return $hours . ":" . $mins . ":" . $secs . '.' . $cents;
} else {
return $hours . ":" . $mins . ":" . $secs;
}
}
/**
* Take the header row of an AICC definition file
* and returns sequence of columns and a pointer to
* the sco identifier column.
*
* @param string $row AICC header row
* @param string $mastername AICC sco identifier column
* @return mixed
*/
function scorm_get_aicc_columns($row, $mastername='system_id') {
$tok = strtok(strtolower($row), "\",\n\r");
$result = new stdClass();
$result->columns = array();
$result->mastercol = 0;
$i = 0;
while ($tok) {
if ($tok != '') {
$result->columns[] = $tok;
if ($tok == $mastername) {
$result->mastercol = $i;
}
$i++;
}
$tok = strtok("\",\n\r");
}
return $result;
}
/**
* Given a colums array return a string containing the regular
* expression to match the columns in a text row.
*
* @param array $column The header columns
* @param string $remodule The regular expression module for a single column
* @return string
*/
function scorm_forge_cols_regexp($columns, $remodule='(".*")?,') {
$regexp = '/^';
foreach ($columns as $column) {
$regexp .= $remodule;
}
$regexp = substr($regexp, 0, -1) . '/';
return $regexp;
}
/**
* Sets up AICC packages
* Called whenever package changes
* @param object $scorm instance - fields are updated and changes saved into database
* @return bool
*/
function scorm_parse_aicc(&$scorm) {
global $DB;
if ($scorm->scormtype == SCORM_TYPE_AICCURL) {
return scorm_aicc_generate_simple_sco($scorm);
}
if (!isset($scorm->cmid)) {
$cm = get_coursemodule_from_instance('scorm', $scorm->id);
$scorm->cmid = $cm->id;
}
$context = context_module::instance($scorm->cmid);
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_scorm', 'content', 0, 'sortorder, itemid, filepath, filename', false);
$version = 'AICC';
$ids = array();
$courses = array();
$extaiccfiles = array('crs', 'des', 'au', 'cst', 'ort', 'pre', 'cmp');
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));
if (!isset($ids[$id])) {
$ids[$id] = new stdClass();
}
$ids[$id]->$extension = $file;
}
}
foreach ($ids as $courseid => $id) {
if (!isset($courses[$courseid])) {
$courses[$courseid] = new stdClass();
}
if (isset($id->crs)) {
$contents = $id->crs->get_content();
$rows = explode("\r\n", $contents);
if (is_array($rows)) {
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)) {
$contents = $id->des->get_content();
$rows = explode("\r\n", $contents);
$columns = scorm_get_aicc_columns($rows[0]);
$regexp = scorm_forge_cols_regexp($columns->columns);
for ($i = 1; $i < count($rows); $i++) {
if (preg_match($regexp, $rows[$i], $matches)) {
for ($j = 0; $j < count($columns->columns); $j++) {
$column = $columns->columns[$j];
if (!isset($courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)])) {
$courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)] = new stdClass();
}
$courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
}
}
}
}
if (isset($id->au)) {
$contents = $id->au->get_content();
$rows = explode("\r\n", $contents);
$columns = scorm_get_aicc_columns($rows[0]);
$regexp = scorm_forge_cols_regexp($columns->columns);
for ($i = 1; $i < count($rows); $i++) {
if (preg_match($regexp, $rows[$i], $matches)) {
for ($j = 0; $j < count($columns->columns); $j++) {
$column = $columns->columns[$j];
$courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1, -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
}
}
}
}
if (isset($id->cst)) {
$contents = $id->cst->get_content();
$rows = explode("\r\n", $contents);
$columns = scorm_get_aicc_columns($rows[0], 'block');
$regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
for ($i = 1; $i < count($rows); $i++) {
if (preg_match($regexp, $rows[$i], $matches)) {
for ($j = 0; $j < count($columns->columns); $j++) {
if ($j != $columns->mastercol) {
$element = substr(trim($matches[$j + 1]), 1 , -1);
if (!empty($element)) {
$courses[$courseid]->elements[$element]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
}
}
}
}
}
}
if (isset($id->ort)) {
$contents = $id->ort->get_content();
$rows = explode("\r\n", $contents);
$columns = scorm_get_aicc_columns($rows[0], 'course_element');
$regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
for ($i = 1; $i < count($rows); $i++) {
if (preg_match($regexp, $rows[$i], $matches)) {
for ($j = 0; $j < count($matches) - 1; $j++) {
if ($j != $columns->mastercol) {
$courses[$courseid]->elements[substr(trim($matches[$j + 1]), 1, -1)]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
}
}
}
}
}
if (isset($id->pre)) {
$contents = $id->pre->get_content();
$rows = explode("\r\n", $contents);
$columns = scorm_get_aicc_columns($rows[0], 'structure_element');
$regexp = scorm_forge_cols_regexp($columns->columns, '(.+),');
for ($i = 1; $i < count($rows); $i++) {
if (preg_match($regexp, $rows[$i], $matches)) {
$elementid = trim($matches[$columns->mastercol + 1]);
$elementid = trim(trim($elementid, '"'), "'"); // Remove any quotes.
$prereq = trim($matches[2 - $columns->mastercol]);
$prereq = trim(trim($prereq, '"'), "'"); // Remove any quotes.
$courses[$courseid]->elements[$elementid]->prerequisites = $prereq;
}
}
}
if (isset($id->cmp)) {
$contents = $id->cmp->get_content();
$rows = explode("\r\n", $contents);
}
}
$oldscoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id));
$sortorder = 0;
$launch = 0;
if (isset($courses)) {
foreach ($courses as $course) {
$sortorder++;
$sco = new stdClass();
$sco->identifier = $course->id;
$sco->scorm = $scorm->id;
$sco->organization = '';
$sco->title = $course->title;
$sco->parent = '/';
$sco->launch = '';
$sco->scormtype = '';
$sco->sortorder = $sortorder;
if ($ss = $DB->get_record('scorm_scoes', array('scorm' => $scorm->id,
'identifier' => $sco->identifier))) {
$id = $ss->id;
$sco->id = $id;
$DB->update_record('scorm_scoes', $sco);
unset($oldscoes[$id]);
} else {
$id = $DB->insert_record('scorm_scoes', $sco);
}
if ($launch == 0) {
$launch = $id;
}
if (isset($course->elements)) {
foreach ($course->elements as $element) {
unset($sco);
$sco = new stdClass();
$sco->identifier = $element->system_id;
$sco->scorm = $scorm->id;
$sco->organization = $course->id;
$sco->title = $element->title;
if (!isset($element->parent)) {
$sco->parent = '/';
} else if (strtolower($element->parent) == 'root') {
$sco->parent = $course->id;
} else {
$sco->parent = $element->parent;
}
$sco->launch = '';
$sco->scormtype = '';
$sco->previous = 0;
$sco->next = 0;
$id = null;
// Is it an Assignable Unit (AU)?
if (isset($element->file_name)) {
$sco->launch = $element->file_name;
$sco->scormtype = 'sco';
}
if ($oldscoid = scorm_array_search('identifier', $sco->identifier, $oldscoes)) {
$sco->id = $oldscoid;
$DB->update_record('scorm_scoes', $sco);
$id = $oldscoid;
$DB->delete_records('scorm_scoes_data', array('scoid' => $oldscoid));
unset($oldscoes[$oldscoid]);
} else {
$id = $DB->insert_record('scorm_scoes', $sco);
}
if (!empty($id)) {
$scodata = new stdClass();
$scodata->scoid = $id;
if (isset($element->web_launch)) {
$scodata->name = 'parameters';
$scodata->value = $element->web_launch;
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
if (isset($element->prerequisites)) {
$scodata->name = 'prerequisites';
$scodata->value = $element->prerequisites;
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
if (isset($element->max_time_allowed)) {
$scodata->name = 'max_time_allowed';
$scodata->value = $element->max_time_allowed;
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
if (isset($element->time_limit_action)) {
$scodata->name = 'time_limit_action';
$scodata->value = $element->time_limit_action;
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
if (isset($element->mastery_score)) {
$scodata->name = 'mastery_score';
$scodata->value = $element->mastery_score;
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
if (isset($element->core_vendor)) {
$scodata->name = 'datafromlms';
$scodata->value = preg_replace('/<cr>/i', "\r\n", $element->core_vendor);
$dataid = $DB->insert_record('scorm_scoes_data', $scodata);
}
}
if ($launch == 0) {
$launch = $id;
}
}
}
}
}
if (!empty($oldscoes)) {
foreach ($oldscoes as $oldsco) {
scorm_delete_tracks($scorm->id, $oldsco->id);
$DB->delete_records('scorm_scoes', ['id' => $oldsco->id]);
}
}
// Find first launchable object.
$sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
// We use get_records here as we need to pass a limit in the query that works cross db.
$scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1);
if (!empty($scoes)) {
$sco = reset($scoes); // We only care about the first record - the above query only returns one.
$scorm->launch = $sco->id;
} else {
$scorm->launch = $launch;
}
$scorm->version = 'AICC';
return true;
}
/**
* Given a scormid creates an AICC Session record to allow HACP
*
* @param int $scormid - id from scorm table
* @return string hacpsession
*/
function scorm_aicc_get_hacp_session($scormid) {
global $USER, $DB, $SESSION;
$cfgscorm = get_config('scorm');
if (empty($cfgscorm->allowaicchacp)) {
return false;
}
$now = time();
$hacpsession = $SESSION->scorm;
$hacpsession->scormid = $scormid;
$hacpsession->hacpsession = random_string(20);
$hacpsession->userid = $USER->id;
$hacpsession->timecreated = $now;
$hacpsession->timemodified = $now;
$DB->insert_record('scorm_aicc_session', $hacpsession);
return $hacpsession->hacpsession;
}
/**
* Check the hacp_session for whether it is valid.
*
* @param string $hacpsession The hacpsession value to check (optional). Normally leave this blank
* and this function will do required_param('sesskey', ...).
* @return mixed - false if invalid, otherwise returns record from scorm_aicc_session table.
*/
function scorm_aicc_confirm_hacp_session($hacpsession) {
global $DB;
$cfgscorm = get_config('scorm');
if (empty($cfgscorm->allowaicchacp)) {
return false;
}
$time = time() - ($cfgscorm->aicchacptimeout * 60);
$sql = "hacpsession = ? AND timemodified > ?";
$hacpsession = $DB->get_record_select('scorm_aicc_session', $sql, array($hacpsession, $time));
if (!empty($hacpsession)) { // Update timemodified as this is still an active session - resets the timeout.
$hacpsession->timemodified = time();
$DB->update_record('scorm_aicc_session', $hacpsession);
}
return $hacpsession;
}
/**
* generate a simple single activity AICC object
* structure to wrap around and externally linked
* AICC package URL
*
* @param object $scorm package record
*/
function scorm_aicc_generate_simple_sco($scorm) {
global $DB;
// Find the oldest one.
$scos = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'id');
if (!empty($scos)) {
$sco = array_shift($scos);
} else {
$sco = new stdClass();
}
// Get rid of old ones.
foreach ($scos as $oldsco) {
scorm_delete_tracks($scorm->id, $oldsco->id);
$DB->delete_records('scorm_scoes', ['id' => $oldsco->id]);
}
$sco->identifier = 'A1';
$sco->scorm = $scorm->id;
$sco->organization = '';
$sco->title = $scorm->name;
$sco->parent = '/';
// Add the HACP signal to the activity launcher.
if (preg_match('/\?/', $scorm->reference)) {
$sco->launch = $scorm->reference.'&CMI=HACP';
} else {
$sco->launch = $scorm->reference.'?CMI=HACP';
}
$sco->scormtype = 'sco';
if (isset($sco->id)) {
$DB->update_record('scorm_scoes', $sco);
$id = $sco->id;
} else {
$id = $DB->insert_record('scorm_scoes', $sco);
}
return $id;
}
/**
* Sets up $userdata array and default values for AICC package.
*
* @param stdClass $userdata an empty stdClass variable that should be set up with user values
* @param object $scorm package record
* @param string $scoid SCO Id
* @param string $attempt attempt number for the user
* @param string $mode scorm display mode type
* @return array The default values that should be used for AICC package
*/
function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {
global $USER;
$aiccuserid = get_config('scorm', 'aiccuserid');
if (!empty($aiccuserid)) {
$userdata->student_id = $USER->id;
} else {
$userdata->student_id = $USER->username;
}
$userdata->student_name = $USER->lastname .', '. $USER->firstname;
if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {
foreach ($usertrack as $key => $value) {
$userdata->$key = $value;
}
} else {
$userdata->status = '';
$userdata->score_raw = '';
}
if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
foreach ($scodatas as $key => $value) {
$userdata->$key = $value;
}
} else {
throw new \moodle_exception('cannotfindsco', 'scorm');
}
if (!$sco = scorm_get_sco($scoid)) {
throw new \moodle_exception('cannotfindsco', 'scorm');
}
$userdata->mode = 'normal';
if (!empty($mode)) {
$userdata->mode = $mode;
}
if ($userdata->mode == 'normal') {
$userdata->credit = 'credit';
} else {
$userdata->credit = 'no-credit';
}
if (isset($userdata->status)) {
if ($userdata->status == '') {
$userdata->entry = 'ab-initio';
} else {
if (isset($userdata->{'cmi.core.exit'}) && ($userdata->{'cmi.core.exit'} == 'suspend')) {
$userdata->entry = 'resume';
} else {
$userdata->entry = '';
}
}
}
$def = array();
$def['cmi.core.student_id'] = $userdata->student_id;
$def['cmi.core.student_name'] = $userdata->student_name;
$def['cmi.core.credit'] = $userdata->credit;
$def['cmi.core.entry'] = $userdata->entry;
$def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');
$def['cmi.core.lesson_mode'] = $userdata->mode;
$def['cmi.student_data.attempt_number'] = scorm_isset($userdata, 'cmi.student_data.attempt_number');
$def['cmi.student_data.mastery_score'] = scorm_isset($userdata, 'mastery_score');
$def['cmi.student_data.max_time_allowed'] = scorm_isset($userdata, 'max_time_allowed');
$def['cmi.student_data.time_limit_action'] = scorm_isset($userdata, 'time_limit_action');
$def['cmi.student_data.tries_during_lesson'] = scorm_isset($userdata, 'cmi.student_data.tries_during_lesson');
$def['cmi.core.lesson_location'] = scorm_isset($userdata, 'cmi.core.lesson_location');
$def['cmi.core.lesson_status'] = scorm_isset($userdata, 'cmi.core.lesson_status');
$def['cmi.core.exit'] = scorm_isset($userdata, 'cmi.core.exit');
$def['cmi.core.score.raw'] = scorm_isset($userdata, 'cmi.core.score.raw');
$def['cmi.core.score.max'] = scorm_isset($userdata, 'cmi.core.score.max');
$def['cmi.core.score.min'] = scorm_isset($userdata, 'cmi.core.score.min');
$def['cmi.core.total_time'] = scorm_isset($userdata, 'cmi.core.total_time', '00:00:00');
$def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');
$def['cmi.comments'] = scorm_isset($userdata, 'cmi.comments');
return $def;
}
+775
View File
@@ -0,0 +1,775 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
echo html_writer::start_tag('script');
?>
<!--// hopefully fool ie IE proof way of getting DOM element
function safeGetElement(doc, el) {
return doc.ids ? doc.ids[el] : doc.getElementById ? doc.getElementById(el) : doc.all[el];
}
// Find elements by class name
var aryClassElements = new Array();
function getNextElementByClassName( strClassName, obj ) {
if ( obj.className == strClassName ) {
aryClassElements[aryClassElements.length] = obj;
}
for ( var i = 0; i < obj.childNodes.length; i++ )
getNextElementByClassName( strClassName, obj.childNodes[i] );
}
function getElementsByClassName( strClassName, obj ) {
aryClassElements = new Array();
getNextElementByClassName( strClassName, obj );
if (aryClassElements.length > 0) {
return aryClassElements[0];
}
else {
return null;
}
}
// retrieve cookie data
function getCookie (cookie_name){
var results = document.cookie.match ( '(^|;) ?' + cookie_name + '=([^;]*)(;|$)' );
if ( results ) {
return (unescape(results[2]));
} else {
return null;
}
}
// retrieve the logging flag from a Cookie
function getLoggingActive () {
var loggingActive = getCookie('SCORMLoggingActive');
if (!loggingActive) {
loggingActive = 'A';
}
return loggingActive;
}
// set the logging flag in a cookie
function setLoggingActive (flag) {
new cookie("SCORMLoggingActive", flag, 365, "/").set();
}
// toggle the logging
function toggleLog () {
if (getLoggingActive() == "A") {
AppendToLog("Moodle Logging Deactivated", 0);
setLoggingActive('N');
logButton.innerHTML = '-><?php echo addslashes_js(get_string('scormloggingoff', 'scorm')); ?>';
} else {
setLoggingActive('A');
AppendToLog("Moodle Logging Activated", 0);
logButton.innerHTML = '<?php echo addslashes_js(get_string('scormloggingon', 'scorm')); ?>';
logPopUpWindow.focus();
}
}
// globals for the log accumulation
var logString = "";
var logRow = 0;
var logPopUpWindow = "N";
var debugSCORMVersion = '<?php echo $scorm->version; ?>';
<?php
$lmsprefix = (scorm_version_check($scorm->version, SCORM_12) || empty($scorm->version)) ? 'LMS' : '';
$lmsapi = (scorm_version_check($scorm->version, SCORM_12) || empty($scorm->version)) ? 'API' : 'API_1484_11';
$lmselements = array();
if (scorm_version_check($scorm->version, SCORM_12) || empty($scorm->version)) {
$lmselements = array( 'cmi.core._children',
'cmi.core.student_id',
'cmi.core.student_name',
'cmi.core.lesson_location',
'cmi.core.credit',
'cmi.core.lesson_status',
'cmi.core.entry',
'cmi.core._children',
'cmi.core.score.raw',
'cmi.core.score.max',
'cmi.core.score.min',
'cmi.core.total_time',
'cmi.core.lesson_mode',
'cmi.core.exit',
'cmi.core.session_time',
'cmi.suspend_data',
'cmi.launch_data',
'cmi.comments',
'cmi.comments_from_lms',
'cmi.objectives._count',
'cmi.objectives._children',
'cmi.objectives.n.id',
'cmi.objectives.n.score._children',
'cmi.objectives.n.score.raw',
'cmi.objectives.n.score.min',
'cmi.objectives.n.score.max',
'cmi.objectives.n.status',
'cmi.student_data._children',
'cmi.student_data.mastery_score',
'cmi.student_data.max_time_allowed',
'cmi.student_data.time_limit_action',
'cmi.student_preference._children',
'cmi.student_preference.audio',
'cmi.student_preference.language',
'cmi.student_preference.speed',
'cmi.student_preference.text',
'cmi.interactions._children',
'cmi.interactions._count',
'cmi.interactions.n.id',
'cmi.interactions.n.objectives._count',
'cmi.interactions.n.objectives.m.id',
'cmi.interactions.n.time',
'cmi.interactions.n.type',
'cmi.interactions.n.correct_responses._count',
'cmi.interactions.n.correct_responses.m.pattern',
'cmi.interactions.n.weighting',
'cmi.interactions.n.student_response',
'cmi.interactions.n.result',
'cmi.interactions.n.latency');
} else {
$lmselements = array( 'cmi._children',
'cmi._version',
'cmi.learner_id',
'cmi.learner_name',
'cmi.location',
'cmi.completion_status',
'cmi.completion_threshold',
'cmi.scaled_passing_score',
'cmi.progressive_measure',
'cmi.score._children',
'cmi.score.raw',
'cmi.score.max',
'cmi.score.min',
'cmi.score.scaled',
'cmi.total_time',
'cmi.time_limit_action',
'cmi.max_time_allowed',
'cmi.session_time',
'cmi.success_status',
'cmi.lesson_mode',
'cmi.entry',
'cmi.exit',
'cmi.credit',
'cmi.mode',
'cmi.suspend_data',
'cmi.launch_data',
'cmi.comments',
'cmi.comments_from_lms._children',
'cmi.comments_from_lms._count',
'cmi.comments_from_lms.n.comment',
'cmi.comments_from_lms.n.location',
'cmi.comments_from_lms.n.timestamp',
'cmi.comments_from_learner._children',
'cmi.comments_from_learner._count',
'cmi.comments_from_learner.n.comment',
'cmi.comments_from_learner.n.location',
'cmi.comments_from_learner.n.timestamp',
'cmi.objectives._count',
'cmi.objectives._children',
'cmi.objectives.n.id',
'cmi.objectives.n.score._children',
'cmi.objectives.n.score.raw',
'cmi.objectives.n.score.min',
'cmi.objectives.n.score.max',
'cmi.objectives.n.score.scaled',
'cmi.objectives.n.success_status',
'cmi.objectives.n.completion_status',
'cmi.objectives.n.progress_measure',
'cmi.objectives.n.description',
'cmi.student_data._children',
'cmi.student_data.mastery_score',
'cmi.student_data.max_time_allowed',
'cmi.student_data.time_limit_action',
'cmi.student_preference._children',
'cmi.student_preference.audio',
'cmi.student_preference.language',
'cmi.student_preference.speed',
'cmi.student_preference.text',
'cmi.interactions._children',
'cmi.interactions._count',
'cmi.interactions.n.id',
'cmi.interactions.n.objectives._count',
'cmi.interactions.n.objectives.m.id',
'cmi.interactions.n.time',
'cmi.interactions.n.type',
'cmi.interactions.n.correct_responses._count',
'cmi.interactions.n.correct_responses.m.pattern',
'cmi.interactions.n.weighting',
'cmi.interactions.n.learner_response',
'cmi.interactions.n.result',
'cmi.interactions.n.latency',
'cmi.interactions.n.description',
'adl.nav.request');
}
?>
// add each entry to the log, or setup the log pane first time round
// The code written into the header is based on the ADL test suite API interaction code
// and various examples of test wrappers out in the community
function UpdateLog(s) {
var s1 = '<html><head><style>\n'
+ 'body {font-family: Arial, Helvetica, Sans-Serif;font-size: xx-small;'
+ 'margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px; '
+ 'background-color: ffffff;}\n'
+ '.even {background-color: ffffff; width: 100%;}\n'
+ '.odd {background-color: e8f2fe; width: 100%;}\n'
+ '.error {background-color: ffffff; color: red; width: 100%;}\n'
+ '<\/style>'
+ '<script>\n'
+ 'var LMSVersion = \'<?php echo $scorm->version; ?>\';\n'
+ ' \n'
+ 'function checkLMSVersion() { \n'
+ ' if (this.document.body.childNodes.length > 0) { \n'
+ ' if (this.document.body.lastChild.id == LMSVersion) { \n'
+ ' return true; \n'
+ ' } \n'
+ ' }; \n'
+ ' alert(\'LMS Version: \' + this.document.body.lastChild.id + \n'
+ ' \' does not equal: \' + LMSVersion + \n'
+ ' \' so API calls will fail - did navigate to another SCORM package?\'); \n'
+ ' return false; \n'
+ '} \n'
+ ' \n'
+ 'var saveElement = ""; \n'
+ 'function setAPIValue() { \n'
+ ' document.elemForm.API_ELEMENT.value = document.elemForm.ELEMENT_LIST.value; \n'
+ ' saveElement = document.elemForm.API_ELEMENT.value; \n'
+ '} \n'
+ ' \n'
+ 'var _Debug = false; // set this to false to turn debugging off \n'
+ ' \n'
+ '// Define exception/error codes \n'
+ 'var _NoError = 0; \n'
+ 'var _GeneralException = 101; \n'
+ 'var _ServerBusy = 102; \n'
+ 'var _InvalidArgumentError = 201; \n'
+ 'var _ElementCannotHaveChildren = 202; \n'
+ 'var _ElementIsNotAnArray = 203; \n'
+ 'var _NotInitialized = 301; \n'
+ 'var _NotImplementedError = 401; \n'
+ 'var _InvalidSetValue = 402; \n'
+ 'var _ElementIsReadOnly = 403; \n'
+ 'var _ElementIsWriteOnly = 404; \n'
+ 'var _IncorrectDataType = 405; \n'
+ ' \n'
+ '// local variable definitions \n'
+ 'var apiHandle = null; \n'
+ 'var API = null; \n'
+ 'var findAPITries = 0; \n'
+ ' \n'
+ ' \n'
+ 'function doLMSInitialize() { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSInitialize was not successful."); \n'
+ ' return "false"; \n'
+ ' } \n'
+ ' var result = api.<?php echo $lmsprefix; ?>Initialize(""); \n'
+ ' if (result.toString() != "true") { \n'
+ ' var err = ErrorHandler(); \n'
+ ' } \n'
+ ' return result.toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSFinish() { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSFinish was not successful."); \n'
+ ' return "false"; \n'
+ ' } else { \n'
+ ' // call the LMSFinish function that should be implemented by the API \n'
+ ' var result = api.<?php echo $lmsprefix; ?>Finish(""); \n'
+ ' if (result.toString() != "true") { \n'
+ ' var err = ErrorHandler(); \n'
+ ' } \n'
+ ' } \n'
+ ' return result.toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSTerminate() { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nTerminate was not successful."); \n'
+ ' return "false"; \n'
+ ' } else { \n'
+ ' // call the Terminate function that should be implemented by the API \n'
+ ' var result = api.Terminate(""); \n'
+ ' if (result.toString() != "true") { \n'
+ ' var err = ErrorHandler(); \n'
+ ' } \n'
+ ' } \n'
+ ' return result.toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSGetValue(name) { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSGetValue was not successful."); \n'
+ ' return ""; \n'
+ ' } else { \n'
+ ' var value = api.<?php echo $lmsprefix; ?>GetValue(name); \n'
+ ' var errCode = api.<?php echo $lmsprefix; ?>GetLastError().toString(); \n'
+ ' if (errCode != _NoError) { \n'
+ ' // an error was encountered so display the error description \n'
+ ' var errDescription = api.<?php echo $lmsprefix; ?>GetErrorString(errCode); \n'
+ ' alert("<?php echo $lmsprefix; ?>GetValue("+name+") failed. \\n"+ errDescription); \n'
+ ' return ""; \n'
+ ' } else { \n'
+ ' return value.toString(); \n'
+ ' } \n'
+ ' } \n'
+ '} \n'
+ ' \n'
+ 'function doLMSSetValue(name, value) { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSSetValue was not successful."); \n'
+ ' return; \n'
+ ' } else { \n'
+ ' var result = api.<?php echo $lmsprefix; ?>SetValue(name, value); \n'
+ ' if (result.toString() != "true") { \n'
+ ' var err = ErrorHandler(); \n'
+ ' } \n'
+ ' } \n'
+ ' return; \n'
+ '} \n'
+ ' \n'
+ 'function doLMSCommit() { \n'
+ ' checkLMSVersion(); \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSCommit was not successful."); \n'
+ ' return "false"; \n'
+ ' } else { \n'
+ ' var result = api.<?php echo $lmsprefix; ?>Commit(""); \n'
+ ' if (result != "true") { \n'
+ ' var err = ErrorHandler(); \n'
+ ' } \n'
+ ' } \n'
+ ' return result.toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSGetLastError() { \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSGetLastError was not successful."); \n'
+ ' //since we can\'t get the error code from the LMS, return a general error \n'
+ ' return _GeneralError; \n'
+ ' } \n'
+ ' return api.<?php echo $lmsprefix; ?>GetLastError().toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSGetErrorString(errorCode) { \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSGetErrorString was not successful."); \n'
+ ' } \n'
+ ' return api.<?php echo $lmsprefix; ?>GetErrorString(errorCode).toString(); \n'
+ '} \n'
+ ' \n'
+ 'function doLMSGetDiagnostic(errorCode) { \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSGetDiagnostic was not successful."); \n'
+ ' } \n'
+ ' return api.<?php echo $lmsprefix; ?>GetDiagnostic(errorCode).toString(); \n'
+ '} \n'
+ ' \n'
+ 'function LMSIsInitialized() { \n'
+ ' // there is no direct method for determining if the LMS API is initialized \n'
+ ' // for example an LMSIsInitialized function defined on the API so we\'ll try \n'
+ ' // a simple LMSGetValue and trap for the LMS Not Initialized Error \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nLMSIsInitialized() failed."); \n'
+ ' return false; \n'
+ ' } else { \n'
+ ' var value = api.<?php echo $lmsprefix; ?>GetValue("cmi.core.student_name"); \n'
+ ' var errCode = api.<?php echo $lmsprefix; ?>GetLastError().toString(); \n'
+ ' if (errCode == _NotInitialized) { \n'
+ ' return false; \n'
+ ' } else { \n'
+ ' return true; \n'
+ ' } \n'
+ ' } \n'
+ '} \n'
+ ' \n'
+ 'function ErrorHandler() { \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\nCannot determine LMS error code."); \n'
+ ' return; \n'
+ ' } \n'
+ ' // check for errors caused by or from the LMS \n'
+ ' var errCode = api.<?php echo $lmsprefix; ?>GetLastError().toString(); \n'
+ ' if (errCode != _NoError) { \n'
+ ' // an error was encountered so display the error description \n'
+ ' var errDescription = api.<?php echo $lmsprefix; ?>GetErrorString(errCode); \n'
+ ' if (_Debug == true) { \n'
+ ' errDescription += "\\n"; \n'
+ ' errDescription += api.<?php echo $lmsprefix; ?>GetDiagnostic(null); \n'
+ ' // by passing null to LMSGetDiagnostic, we get any available diagnostics \n'
+ ' // on the previous error. \n'
+ ' } \n'
+ ' alert(errDescription); \n'
+ ' } \n'
+ ' return errCode; \n'
+ '} \n'
+ ' \n'
+ 'function getAPIHandle() { \n'
+ ' if (apiHandle == null) { \n'
+ ' apiHandle = getAPI(); \n'
+ ' } \n'
+ ' return apiHandle; \n'
+ '} \n'
+ ' \n'
+ 'function findAPI(win) { \n'
+ ' while ((win.<?php echo $lmsapi; ?> == null) && (win.parent != null) && (win.parent != win)) { \n'
+ ' findAPITries++; \n'
+ ' // Note: 7 is an arbitrary number, but should be more than sufficient \n'
+ ' if (findAPITries > 7) { \n'
+ ' alert("Error finding API -- too deeply nested."); \n'
+ ' return null; \n'
+ ' } \n'
+ ' win = win.parent; \n'
+ ' } \n'
+ ' return win.<?php echo $lmsapi; ?>; \n'
+ '} \n'
+ ' \n'
+ 'function getAPI() { \n'
+ ' var theAPI = findAPI(window); \n'
+ ' if ((theAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined")) { \n'
+ ' theAPI = findAPI(window.opener); \n'
+ ' } \n'
+ ' if (theAPI == null) { \n'
+ ' alert("Unable to find an API adapter"); \n'
+ ' } \n'
+ ' return theAPI \n'
+ '} \n'
+ ' \n'
+ ' function tryLMSInitialize() { \n'
+ ' var result = doLMSInitialize(); \n'
+ ' var msg; \n'
+ ' if(result == "true") { \n'
+ ' msg = "<?php echo $lmsprefix; ?>Initialize Successful!"; \n'
+ ' } else { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' msg = "<?php echo $lmsprefix; ?>Initialize Failed! Error Code: "+err; \n'
+ ' msg += " Error Description: " + errString; \n'
+ ' } \n'
+ ' document.initForm.msgtxt.value= msg; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSCommit() { \n'
+ ' var result = doLMSCommit(); \n'
+ ' var msg; \n'
+ ' if(result == "true") { \n'
+ ' msg = "<?php echo $lmsprefix; ?>Commit was Successful!"; \n'
+ ' } else { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' var msg = "<?php echo $lmsprefix; ?>Commit Failed! Error Code: "+err; \n'
+ ' msg += " Error Description: " + errString; \n'
+ ' } \n'
+ ' document.otherForm.msgtxt.value = msg; \n'
+ ' document.elemForm.API_ELEMENT.value = saveElement; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSFinish() { \n'
+ ' // set now, in case the SCO is unloaded on LMSFinish \n'
+ ' doLMSSetValue("cmi.core.lesson_status", "completed"); \n'
+ ' doLMSSetValue("cmi.core.exit", ""); \n'
+ ' doLMSSetValue("cmi.core.session_time", "00:00:30"); \n'
+ ' var result = doLMSFinish(); \n'
+ ' var msg; \n'
+ ' if(result == "true") { \n'
+ ' msg = "LMSFinish Successful!"; \n'
+ ' document.otherForm.msgtxt.value = msg; \n'
+ ' } else { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' var msg = "LMSFinish Failed! Error Code: "+err; \n'
+ ' msg += " Error Description: " + errString; \n'
+ ' document.otherForm.msgtxt.value = msg; \n'
+ ' } \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSTerminate() { \n'
+ ' var result = doLMSTerminate(); \n'
+ ' var msg; \n'
+ ' if(result == "true") { \n'
+ ' msg = "Terminate Successful!"; \n'
+ ' document.otherForm.msgtxt.value = msg; \n'
+ ' } else { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' var msg = "Terminate Failed! Error Code: "+err; \n'
+ ' msg += " Error Description: " + errString; \n'
+ ' document.otherForm.msgtxt.value = msg; \n'
+ ' } \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSGetValue() { \n'
+ ' var value = document.elemForm.API_ELEMENT.value; \n'
+ ' var msg; \n'
+ ' var result = doLMSGetValue(value); \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' msg = "<?php echo $lmsprefix; ?>GetValue Returned: " + result; \n'
+ ' msg += "\\nError Code: " + err; \n'
+ ' msg += "\\nError Description: " + errString; \n'
+ ' document.elemForm.msgtxt.value = msg; \n'
+ ' document.elemForm.API_ELEMENT.value = saveElement; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSSetValue() { \n'
+ ' // Get the element that is to be set \n'
+ ' var setValue = document.elemForm.SET_VAL.value; \n'
+ ' var item = document.elemForm.API_ELEMENT.value; \n'
+ ' var msg; \n'
+ ' var api = getAPIHandle(); \n'
+ ' if (api == null) { \n'
+ ' alert("Unable to locate the LMS\'s API Implementation.\\n"+ \n'
+ ' "<?php echo $lmsprefix; ?>SetValue was not successful."); \n'
+ ' return false; \n'
+ ' } \n'
+ ' // Try to set the element \n'
+ ' var result = api.<?php echo $lmsprefix; ?>SetValue( item, setValue ); \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' msg = "<?php echo $lmsprefix; ?>SetValue returned: " + result; \n'
+ ' msg += "\\nError Code: " + err; \n'
+ ' msg += "\\nError Description: " + errString; \n'
+ ' document.elemForm.msgtxt.value = msg; \n'
+ ' document.elemForm.API_ELEMENT.value = saveElement; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSGetLastError() { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' document.otherForm.msgtxt.value = "<?php echo $lmsprefix; ?>GetLastError returned Error Code: " + err; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSGetErrorString() { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var errString = doLMSGetErrorString(err); \n'
+ ' document.otherForm.msgtxt.value = "<?php echo $lmsprefix; ?>GetErrorString returned: " + errString; \n'
+ ' } \n'
+ ' \n'
+ ' function tryLMSGetDiagnostic() { \n'
+ ' var err = doLMSGetLastError(); \n'
+ ' var diagnostic = doLMSGetDiagnostic(err); \n'
+ ' document.otherForm.msgtxt.value = "<?php echo $lmsprefix; ?>GetDiagnostic returned: " + diagnostic; \n'
+ ' } \n'
+ ' \n'
+ '</script>\n'
+ '<\/head><body STYLE="background-color: ffffff; color: black"'
+ 'marginwidth="0" leftmargin="0" hspace="0">'
+ '<h1>SCORM Debugging interface</h1>'
+ '<h2>SCORM Version Detected: <?php echo $scorm->version; ?></h2>'
+ '<input type="hidden" id="mod-scorm-logstate" name="mod-scorm-logstate" value="A" \/>'
+ '<form name="initForm" onsubmit="return false;">'
+ ' <table width="100%" border="0">'
+ ' <tr>'
+ ' <td>'
+ ' <input type = "button" value = "Call <?php echo $lmsprefix; ?>Initialize()"'
+ ' onclick = "tryLMSInitialize();" id="Initialize" name="Initialize" />'
+ ' </td>'
+ ' <td>'
+ ' <label>Result: </label><input type="text" name="msgtxt" id="msgtxt" size="80" readonly value="NotCalled" />'
+ ' </td>'
+ ' </tr>'
+ ' </table>'
+ '</form>'
+ '<hr />'
+ '<form name="elemForm" id="elemForm" onsubmit="return false;">'
+ ' <table width="100%" border="0">'
+ ' <tr>'
+ ' <td><b>Select Data Model Element to Get or Set</b> &nbsp;&nbsp;&nbsp;&nbsp;'
+ ' <select name = "ELEMENT_LIST" id="ELEMENT_LIST" onchange="setAPIValue()">'
+ ' <option value="NONE">--None Selected--</option>'
+ ' <option value="">******************************************</option>'
<?php
foreach ($lmselements as $element) {
echo ' + \' <option value="'.$element.'">'.$element.'</option>\\n\'';
}
?>
+ ' </select>'
+ ' <input type="text" name="API_ELEMENT" id="API_ELEMENT" size="40"><br />'
+ ' <br />'
+ ' <label><b>Select API Function to Call</b></label> &nbsp;&nbsp;&nbsp;&nbsp;'
+ ' <input type = "button" value = "<?php echo $lmsprefix; ?>GetValue()"'
+ ' onclick = "tryLMSGetValue();" id="lmsGetButton"'
+ ' name="lmsGetButton">&nbsp;&nbsp;-- OR --&nbsp;&nbsp;'
+ ' <input type="button" value="<?php echo $lmsprefix; ?>SetValue()"'
+ ' onclick="tryLMSSetValue();" id="lmsSetButton"'
+ ' name="lmsSetButton">'
+ ' <label><b>&nbsp; value to Set: </b></label>&nbsp; <input type="text" name="SET_VAL" id="SET_VAL" size="25">'
+ ' <br />'
+ ' <label>Result: </label><br />'
+ ' <textarea name="msgtxt" id="msgtxt" rows="2" cols="150" wrap="VIRTUAL" readonly>None</textarea>'
+ ' </td>'
+ ' </tr>'
+ ' </table>'
+ '</form>'
+ '<hr />'
+ '<form name="otherForm" onsubmit="return false;">'
+ ' <h3>Additional API Functions</h3>'
+ ' <table width="100%" border="0">'
+ ' <tr>'
+ ' <td><input type="button"'
+ ' value="<?php echo $lmsprefix; ?>GetLastError() "'
+ ' onclick="tryLMSGetLastError();"'
+ ' id="lastErrorButton"'
+ ' name="lastErrorButton">'
+ ' <input type="button"'
+ ' value="<?php echo $lmsprefix; ?>GetErrorString() "'
+ ' onclick="tryLMSGetErrorString();"'
+ ' id="getErrorStringButton"'
+ ' name="getErrorStringButton">'
+ ' <input type="button"'
+ ' value="<?php echo $lmsprefix; ?>GetDiagnostic() "'
+ ' onclick="tryLMSGetDiagnostic();"'
+ ' id="getDiagnosticButton"'
+ ' name="getDiagnosticButton">'
+ ' <input type="button"'
+ ' value="<?php echo $lmsprefix; ?>Commit() "'
+ ' onclick="tryLMSCommit();"'
+ ' id="commitButton"'
+ ' name="commitButton">'
+ ' <input type="button"'
+ ' value="<?php echo scorm_version_check($scorm->version, SCORM_12) ? 'LMSFinish' : 'Terminate'; ?>() "'
+ ' onclick="try'
+ ' <?php echo scorm_version_check($scorm->version, SCORM_12) ? 'LMSFinish' : 'LMSTerminate'; ?>();"'
+ ' id="finishButton"'
+ ' name="finishButton">'
+ ' </td>'
+ ' </tr>'
+ ' <tr>'
+ ' <td>'
+ ' <label>Result: </label><br />'
+ ' <textarea name="msgtxt" id="msgtxt" rows="2" cols="150" wrap="VIRTUAL" readonly>None</textarea>'
+ ' </td>'
+ ' </tr>'
+ ' </table>'
+ '</form>'
+ '<h3 id="mod-scorm-marker">SCORM API Activity Log<\/h3>';
// Is logging active?
if (getLoggingActive() != "A") {
return;
}
var popupdoc = '';
logString += s;
if (logPopUpWindow != 'N' && !logPopUpWindow.closed) {
popupdoc = logPopUpWindow.document;
popupdoc.body.innerHTML += s;
} else {
logPopUpWindow = open( '', 'scormlogpopupwindow', '' );
popupdoc = logPopUpWindow.document;
// Is logging active?
var marker = safeGetElement(popupdoc, 'mod-scorm-marker');
if (marker) {
popupdoc.body.innerHTML += s;
} else {
popupdoc.open();
popupdoc.write(s1);
popupdoc.write(logString);
popupdoc.write('<\/body><\/html>')
popupdoc.close();
popupdoc.title = 'SCORM API Activity Log';
logPopUpWindow.focus();
}
}
if (popupdoc.body && popupdoc.body.childNodes.length > 0) {
popupdoc.body.lastChild.scrollIntoView();
};
}
//add an individual log entry
function AppendToLog(s, rc) {
var sStyle = '';
if (rc != 0) {
sStyle = 'class="error"';
} else if (logRow % 2 != 0) {
sStyle = 'class="even"';
} else {
sStyle = 'class="odd"';
}
var now = new Date();
now.setTime( now.getTime() );
s = '<div ' + sStyle + ' id="<?php echo $scorm->version; ?>">' + now.toGMTString() + ': ' + s + '<\/div>';
UpdateLog(s);
// switch colours for a new section of work
if (s.match(/Commit|Loaded|Initialize|Terminate|Finish|Moodle SCORM|Moodle Logging/)) {
logRow++;
}
}
// format a log entry
function LogAPICall(func, nam, val, rc) {
// drop call to GetLastError for the time being - it produces too much chatter
if (func.match(/GetLastError/)) {
return;
}
var s = func + '("' + nam + '"';
if (val != null && ! (func.match(/GetValue|GetLastError/))) {
s += ', "' + val + '"';
}
s += ')';
if (func.match(/GetValue/)) {
s += ' - ' + val;
}
s += ' => ' + String(rc) + " scoid = " + scorm_current_node.scoid;
AppendToLog(s, rc);
<?php
if (scorm_debugging($scorm) && ($sco->scormtype == 'asset')) {
?>
hint = 'Item <?php echo $sco->identifier; ?> has been defined as an Asset: it should never call the SCORM API';
AppendToLog(hint, 101);
<?php
}
?>
}
// Add in a JS controlled link for toggling the Debug logging
if (!document.getElementById('mod-scorm-log-toggle')) {
var logButton = document.createElement('a');
logButton.id = 'mod-scorm-log-toggle';
logButton.name = 'logToggle';
logButton.href = 'javascript:toggleLog();';
if (getLoggingActive() == "A") {
logButton.innerHTML = '<?php echo addslashes_js(get_string('scormloggingon', 'scorm')); ?>';
} else {
logButton.innerHTML = '<?php echo addslashes_js(get_string('scormloggingoff', 'scorm')); ?>';
}
var content = safeGetElement(document, 'scormpage');
content.insertBefore(logButton, content.firstChild);
}
-->
<?php
echo html_writer::end_tag('script');
+678
View File
@@ -0,0 +1,678 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
//
// SCORM 1.2 API Implementation
//
function SCORMapi1_2(def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot, sesskey,
scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride, hidetoc) {
var prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
var datamodelurl = cfgwwwroot + "/mod/scorm/datamodel.php";
var datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
// Standard Data Type Definition
CMIString256 = cmistring256;
CMIString4096 = cmistring4096;
CMITime = '^([0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\.[0-9]{1,2})?$';
CMITimespan = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$';
CMIInteger = '^\\d+$';
CMISInteger = '^-?([0-9]+)$';
CMIDecimal = '^-?([0-9]{0,3})(\.[0-9]*)?$';
CMIIdentifier = '^[\\u0021-\\u007E]{0,255}$';
CMIFeedback = CMIString256; // This must be redefined
CMIIndex = '[._](\\d+).';
// Vocabulary Data Type Definition
CMIStatus = '^passed$|^completed$|^failed$|^incomplete$|^browsed$';
CMIStatus2 = '^passed$|^completed$|^failed$|^incomplete$|^browsed$|^not attempted$';
CMIExit = '^time-out$|^suspend$|^logout$|^$';
CMIType = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$';
CMIResult = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\.[0-9]*)?$';
NAVEvent = '^previous$|^continue$';
// Children lists
cmi_children = 'core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions';
core_children = 'student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time';
score_children = 'raw,min,max';
comments_children = 'content,location,time';
objectives_children = 'id,score,status';
correct_responses_children = 'pattern';
student_data_children = 'mastery_score,max_time_allowed,time_limit_action';
student_preference_children = 'audio,language,speed,text';
interactions_children = 'id,objectives,time,type,correct_responses,weighting,student_response,result,latency';
// Data ranges
score_range = '0#100';
audio_range = '-1#100';
speed_range = '-100#100';
weighting_range = '-100#100';
text_range = '-1#1';
// The SCORM 1.2 data model
// Set up data model for each sco
var datamodel = {};
for(scoid in def){
datamodel[scoid] = {
'cmi._children':{'defaultvalue':cmi_children, 'mod':'r', 'writeerror':'402'},
'cmi._version':{'defaultvalue':'3.4', 'mod':'r', 'writeerror':'402'},
'cmi.core._children':{'defaultvalue':core_children, 'mod':'r', 'writeerror':'402'},
'cmi.core.student_id':{'defaultvalue':def[scoid]['cmi.core.student_id'], 'mod':'r', 'writeerror':'403'},
'cmi.core.student_name':{'defaultvalue':def[scoid]['cmi.core.student_name'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_location':{'defaultvalue':def[scoid]['cmi.core.lesson_location'], 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.core.credit':{'defaultvalue':def[scoid]['cmi.core.credit'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_status':{'defaultvalue':def[scoid]['cmi.core.lesson_status'], 'format':CMIStatus, 'mod':'rw', 'writeerror':'405'},
'cmi.core.entry':{'defaultvalue':def[scoid]['cmi.core.entry'], 'mod':'r', 'writeerror':'403'},
'cmi.core.score._children':{'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'},
'cmi.core.score.raw':{'defaultvalue':def[scoid]['cmi.core.score.raw'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.score.max':{'defaultvalue':def[scoid]['cmi.core.score.max'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.score.min':{'defaultvalue':def[scoid]['cmi.core.score.min'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.core.total_time':{'defaultvalue':def[scoid]['cmi.core.total_time'], 'mod':'r', 'writeerror':'403'},
'cmi.core.lesson_mode':{'defaultvalue':def[scoid]['cmi.core.lesson_mode'], 'mod':'r', 'writeerror':'403'},
'cmi.core.exit':{'defaultvalue':def[scoid]['cmi.core.exit'], 'format':CMIExit, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.core.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'00:00:00', 'readerror':'404', 'writeerror':'405'},
'cmi.suspend_data':{'defaultvalue':def[scoid]['cmi.suspend_data'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
'cmi.launch_data':{'defaultvalue':def[scoid]['cmi.launch_data'], 'mod':'r', 'writeerror':'403'},
'cmi.comments':{'defaultvalue':def[scoid]['cmi.comments'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
// deprecated evaluation attributes
'cmi.evaluation.comments._count':{'defaultvalue':'0', 'mod':'r', 'writeerror':'402'},
'cmi.evaluation.comments._children':{'defaultvalue':comments_children, 'mod':'r', 'writeerror':'402'},
'cmi.evaluation.comments.n.content':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.evaluation.comments.n.location':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.evaluation.comments.n.time':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMITime, 'mod':'rw', 'writeerror':'405'},
'cmi.comments_from_lms':{'mod':'r', 'writeerror':'403'},
'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r', 'writeerror':'402'},
'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'mod':'r', 'writeerror':'402'},
'cmi.objectives.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
'cmi.objectives.n.status':{'pattern':CMIIndex, 'format':CMIStatus2, 'mod':'rw', 'writeerror':'405'},
'cmi.student_data._children':{'defaultvalue':student_data_children, 'mod':'r', 'writeerror':'402'},
'cmi.student_data.mastery_score':{'defaultvalue':def[scoid]['cmi.student_data.mastery_score'], 'mod':'r', 'writeerror':'403'},
'cmi.student_data.max_time_allowed':{'defaultvalue':def[scoid]['cmi.student_data.max_time_allowed'], 'mod':'r', 'writeerror':'403'},
'cmi.student_data.time_limit_action':{'defaultvalue':def[scoid]['cmi.student_data.time_limit_action'], 'mod':'r', 'writeerror':'403'},
'cmi.student_preference._children':{'defaultvalue':student_preference_children, 'mod':'r', 'writeerror':'402'},
'cmi.student_preference.audio':{'defaultvalue':def[scoid]['cmi.student_preference.audio'], 'format':CMISInteger, 'range':audio_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.language':{'defaultvalue':def[scoid]['cmi.student_preference.language'], 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.speed':{'defaultvalue':def[scoid]['cmi.student_preference.speed'], 'format':CMISInteger, 'range':speed_range, 'mod':'rw', 'writeerror':'405'},
'cmi.student_preference.text':{'defaultvalue':def[scoid]['cmi.student_preference.text'], 'format':CMISInteger, 'range':text_range, 'mod':'rw', 'writeerror':'405'},
'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r', 'writeerror':'402'},
'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.correct_responses._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':weighting_range, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.student_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
'nav.event':{'defaultvalue':'', 'format':NAVEvent, 'mod':'w', 'readerror':'404', 'writeerror':'405'}
};
}
var cmi, nav;
function initdatamodel(scoid){
prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
//
// Datamodel inizialization
//
cmi = new Object();
cmi.core = new Object();
cmi.core.score = new Object();
cmi.objectives = new Object();
cmi.student_data = new Object();
cmi.student_preference = new Object();
cmi.interactions = new Object();
// deprecated evaluation attributes
cmi.evaluation = new Object();
cmi.evaluation.comments = new Object();
// Navigation Object
nav = new Object();
for (element in datamodel[scoid]) {
if (element.match(/\.n\./) == null) {
if (typeof datamodel[scoid][element].defaultvalue != 'undefined') {
eval(element + ' = datamodel["' + scoid + '"]["' + element + '"].defaultvalue;');
} else {
eval(element + ' = "";');
}
}
}
eval(cmiobj[scoid]);
eval(cmiint[scoid]);
if (cmi.core.lesson_status == '') {
cmi.core.lesson_status = 'not attempted';
}
}
//
// API Methods definition
//
var Initialized = false;
function LMSInitialize (param) {
scoid = (scorm_current_node && scorm_current_node.scoid) ? scorm_current_node.scoid : scoid;
initdatamodel(scoid);
errorCode = "0";
if (param == "") {
if (!Initialized) {
Initialized = true;
errorCode = "0";
if (scormdebugging) {
LogAPICall("LMSInitialize", param, "", errorCode);
}
return "true";
} else {
errorCode = "101";
}
} else {
errorCode = "201";
}
if (scormdebugging) {
LogAPICall("LMSInitialize", param, "", errorCode);
}
return "false";
}
function LMSFinish (param) {
errorCode = "0";
if (param == "") {
if (Initialized) {
Initialized = false;
result = StoreData(cmi,true);
if (nav.event != '') {
if (nav.event == 'continue') {
setTimeout('mod_scorm_launch_next_sco();',500);
} else {
setTimeout('mod_scorm_launch_prev_sco();',500);
}
} else {
if (scormauto == 1) {
setTimeout('mod_scorm_launch_next_sco();',500);
}
}
if (scormdebugging) {
LogAPICall("LMSFinish", "AJAXResult", result, 0);
}
result = ('true' == result) ? 'true' : 'false';
errorCode = (result == 'true') ? '0' : '101';
if (scormdebugging) {
LogAPICall("LMSFinish", "result", result, 0);
LogAPICall("LMSFinish", param, "", 0);
}
// trigger TOC update
var callback = M.mod_scorm.connectPrereqCallback;
YUI().use('io-base', function(Y) {
Y.on('io:complete', callback.success, Y);
Y.io(prerequrl);
});
return result;
} else {
errorCode = "301";
}
} else {
errorCode = "201";
}
if (scormdebugging) {
LogAPICall("LMSFinish", param, "", errorCode);
}
return "false";
}
function LMSGetValue (element) {
errorCode = "0";
if (Initialized) {
if (element != "") {
expression = new RegExp(CMIIndex,'g');
elementmodel = String(element).replace(expression,'.n.');
if (typeof datamodel[scoid][elementmodel] != "undefined") {
if (datamodel[scoid][elementmodel].mod != 'w') {
element = String(element).replace(expression, "_$1.");
elementIndexes = element.split('.');
subelement = 'cmi';
i = 1;
while ((i < elementIndexes.length) && (typeof eval(subelement) != "undefined")) {
subelement += '.' + elementIndexes[i++];
}
if (subelement == element) {
errorCode = "0";
if (scormdebugging) {
LogAPICall("LMSGetValue", element, eval(element), 0);
}
return eval(element);
} else {
errorCode = "0"; // Need to check if it is the right errorCode
}
} else {
errorCode = datamodel[scoid][elementmodel].readerror;
}
} else {
childrenstr = '._children';
countstr = '._count';
if (elementmodel.substr(elementmodel.length - childrenstr.length,elementmodel.length) == childrenstr) {
parentmodel = elementmodel.substr(0,elementmodel.length - childrenstr.length);
if (typeof datamodel[scoid][parentmodel] != "undefined") {
errorCode = "202";
} else {
errorCode = "201";
}
} else if (elementmodel.substr(elementmodel.length - countstr.length,elementmodel.length) == countstr) {
parentmodel = elementmodel.substr(0,elementmodel.length - countstr.length);
if (typeof datamodel[scoid][parentmodel] != "undefined") {
errorCode = "203";
} else {
errorCode = "201";
}
} else {
errorCode = "201";
}
}
} else {
errorCode = "201";
}
} else {
errorCode = "301";
}
if (scormdebugging) {
LogAPICall("LMSGetValue", element, "", errorCode);
}
return "";
}
function LMSSetValue (element,value) {
errorCode = "0";
if (Initialized) {
if (element != "") {
expression = new RegExp(CMIIndex,'g');
elementmodel = String(element).replace(expression,'.n.');
if (typeof datamodel[scoid][elementmodel] != "undefined") {
if (datamodel[scoid][elementmodel].mod != 'r') {
expression = new RegExp(datamodel[scoid][elementmodel].format);
value = value + '';
matches = value.match(expression);
if (matches != null) {
//Create dynamic data model element
if (element != elementmodel) {
elementIndexes = element.split('.');
subelement = 'cmi';
for (i = 1; i < elementIndexes.length - 1; i++) {
elementIndex = elementIndexes[i];
if (elementIndexes[i + 1].match(/^\d+$/)) {
if ((typeof eval(subelement + '.' + elementIndex)) == "undefined") {
eval(subelement + '.' + elementIndex + ' = new Object();');
eval(subelement + '.' + elementIndex + '._count = 0;');
}
if (elementIndexes[i + 1] == eval(subelement + '.' + elementIndex + '._count')) {
eval(subelement + '.' + elementIndex + '._count++;');
}
if (elementIndexes[i + 1] > eval(subelement + '.' + elementIndex + '._count')) {
errorCode = "201";
}
subelement = subelement.concat('.' + elementIndex + '_' + elementIndexes[i + 1]);
i++;
} else {
subelement = subelement.concat('.' + elementIndex);
}
if ((typeof eval(subelement)) == "undefined") {
eval(subelement + ' = new Object();');
if (subelement.substr(0,14) == 'cmi.objectives') {
eval(subelement + '.score = new Object();');
eval(subelement + '.score._children = score_children;');
eval(subelement + '.score.raw = "";');
eval(subelement + '.score.min = "";');
eval(subelement + '.score.max = "";');
}
if (subelement.substr(0,16) == 'cmi.interactions') {
eval(subelement + '.objectives = new Object();');
eval(subelement + '.objectives._count = 0;');
eval(subelement + '.correct_responses = new Object();');
eval(subelement + '.correct_responses._count = 0;');
}
}
}
element = subelement.concat('.' + elementIndexes[elementIndexes.length - 1]);
}
//Store data
if (errorCode == "0") {
if (autocommit && !(SCORMapi1_2.timeout)) {
SCORMapi1_2.timeout = Y.later(60000, API, 'LMSCommit', [""], false);
}
if (typeof datamodel[scoid][elementmodel].range != "undefined") {
range = datamodel[scoid][elementmodel].range;
ranges = range.split('#');
value = value * 1.0;
if ((value >= ranges[0]) && (value <= ranges[1])) {
eval(element + '=value;');
errorCode = "0";
if (scormdebugging) {
LogAPICall("LMSSetValue", element, value, errorCode);
}
return "true";
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
if (element == 'cmi.comments') {
cmi.comments = cmi.comments + value;
} else {
eval(element + '=value;');
}
errorCode = "0";
if (scormdebugging) {
LogAPICall("LMSSetValue", element, value, errorCode);
}
return "true";
}
}
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
errorCode = datamodel[scoid][elementmodel].writeerror;
}
} else {
errorCode = "201"
}
} else {
errorCode = "201";
}
} else {
errorCode = "301";
}
if (scormdebugging) {
LogAPICall("LMSSetValue", element, value, errorCode);
}
return "false";
}
function LMSCommit (param) {
if (SCORMapi1_2.timeout) {
SCORMapi1_2.timeout.cancel();
SCORMapi1_2.timeout = null;
}
errorCode = "0";
if (param == "") {
if (Initialized) {
result = StoreData(cmi,false);
// Trigger TOC update only if TOC is displayed.
// Checks against setting Display course structure in player:
// 0 = To the side, 1 = Hidden, 2 = In a drop down menu, 3 = Disabled
if (hidetoc !== '3') {
Y.log('Refreshing toc');
var callback = M.mod_scorm.connectPrereqCallback;
YUI().use('io-base', function(Y) {
Y.on('io:complete', callback.success, Y);
Y.io(prerequrl);
});
}
if (scormdebugging) {
LogAPICall("Commit", param, "", 0);
}
if (scormdebugging) {
LogAPICall("LMSCommit", "AJAXResult", result, 0);
}
result = ('true' == result) ? 'true' : 'false';
errorCode = (result == 'true') ? '0' : '101';
if (scormdebugging) {
LogAPICall("LMSCommit", "result", result, 0);
LogAPICall("LMSCommit", "errorCode", errorCode, 0);
}
return result;
} else {
errorCode = "301";
}
} else {
errorCode = "201";
}
if (scormdebugging) {
LogAPICall("LMSCommit", param, "", 0);
}
return "false";
}
function LMSGetLastError () {
if (scormdebugging) {
LogAPICall("LMSGetLastError", "", "", errorCode);
}
return errorCode;
}
function LMSGetErrorString (param) {
if (param != "") {
var errorString = new Array();
errorString["0"] = "No error";
errorString["101"] = "General exception";
errorString["201"] = "Invalid argument error";
errorString["202"] = "Element cannot have children";
errorString["203"] = "Element not an array - cannot have count";
errorString["301"] = "Not initialized";
errorString["401"] = "Not implemented error";
errorString["402"] = "Invalid set value, element is a keyword";
errorString["403"] = "Element is read only";
errorString["404"] = "Element is write only";
errorString["405"] = "Incorrect data type";
if (scormdebugging) {
LogAPICall("LMSGetErrorString", param, errorString[param], 0);
}
return errorString[param];
} else {
if (scormdebugging) {
LogAPICall("LMSGetErrorString", param, "No error string found!", 0);
}
return "";
}
}
function LMSGetDiagnostic (param) {
if (param == "") {
param = errorCode;
}
if (scormdebugging) {
LogAPICall("LMSGetDiagnostic", param, param, 0);
}
return param;
}
function AddTime (first, second) {
var sFirst = first.split(":");
var sSecond = second.split(":");
var cFirst = sFirst[2].split(".");
var cSecond = sSecond[2].split(".");
var change = 0;
FirstCents = 0; //Cents
if (cFirst.length > 1) {
FirstCents = parseInt(cFirst[1],10);
}
SecondCents = 0;
if (cSecond.length > 1) {
SecondCents = parseInt(cSecond[1],10);
}
var cents = FirstCents + SecondCents;
change = Math.floor(cents / 100);
cents = cents - (change * 100);
if (Math.floor(cents) < 10) {
cents = "0" + cents.toString();
}
var secs = parseInt(cFirst[0],10) + parseInt(cSecond[0],10) + change; //Seconds
change = Math.floor(secs / 60);
secs = secs - (change * 60);
if (Math.floor(secs) < 10) {
secs = "0" + secs.toString();
}
mins = parseInt(sFirst[1],10) + parseInt(sSecond[1],10) + change; //Minutes
change = Math.floor(mins / 60);
mins = mins - (change * 60);
if (mins < 10) {
mins = "0" + mins.toString();
}
hours = parseInt(sFirst[0],10) + parseInt(sSecond[0],10) + change; //Hours
if (hours < 10) {
hours = "0" + hours.toString();
}
if (cents != '0') {
return hours + ":" + mins + ":" + secs + '.' + cents;
} else {
return hours + ":" + mins + ":" + secs;
}
}
function TotalTime() {
total_time = AddTime(cmi.core.total_time, cmi.core.session_time);
return '&' + underscore('cmi.core.total_time') + '=' + encodeURIComponent(total_time);
}
function CollectData(data,parent) {
var datastring = '';
for (property in data) {
if (typeof data[property] == 'object') {
datastring += CollectData(data[property],parent + '.' + property);
} else {
element = parent + '.' + property;
expression = new RegExp(CMIIndex,'g');
// get the generic name for this element (e.g. convert 'cmi.interactions.1.id' to 'cmi.interactions.n.id')
elementmodel = String(element).replace(expression,'.n.');
// ignore the session time element
if (element != "cmi.core.session_time") {
// check if this specific element is not defined in the datamodel,
// but the generic element name is
if (typeof datamodel[scoid][element] == "undefined" && typeof datamodel[scoid][elementmodel] != "undefined") {
// add this specific element to the data model (by cloning
// the generic element) so we can track changes to it
datamodel[scoid][element] = CloneObj(datamodel[scoid][elementmodel]);
}
// check if the current element exists in the datamodel
if (typeof datamodel[scoid][element] != "undefined") {
// make sure this is not a read only element
if (datamodel[scoid][element].mod != 'r') {
elementstring = '&' + underscore(element) + '=' + encodeURIComponent(data[property]);
// check if the element has a default value
if (typeof datamodel[scoid][element].defaultvalue != "undefined") {
// check if the default value is different from the current value
if (datamodel[scoid][element].defaultvalue != data[property] ||
typeof datamodel[scoid][element].defaultvalue != typeof data[property]) {
// append the URI fragment to the string we plan to commit
datastring += elementstring;
// update the element default to reflect the current committed value
datamodel[scoid][element].defaultvalue = data[property];
}
} else {
// append the URI fragment to the string we plan to commit
datastring += elementstring;
// no default value for the element, so set it now
datamodel[scoid][element].defaultvalue = data[property];
}
}
}
}
}
}
return datastring;
}
function CloneObj(obj){
if(obj == null || typeof(obj) != 'object') {
return obj;
}
var temp = new obj.constructor(); // changed (twice)
for(var key in obj) {
temp[key] = CloneObj(obj[key]);
}
return temp;
}
function StoreData(data,storetotaltime) {
var datastring = '';
if (storetotaltime) {
if (cmi.core.lesson_status == 'not attempted') {
cmi.core.lesson_status = 'completed';
}
if (cmi.core.lesson_mode == 'normal') {
if (cmi.core.credit == 'credit') {
if (masteryoverride && cmi.student_data.mastery_score !== '' && cmi.core.score.raw !== '') {
if (parseFloat(cmi.core.score.raw) >= parseFloat(cmi.student_data.mastery_score)) {
cmi.core.lesson_status = 'passed';
} else {
cmi.core.lesson_status = 'failed';
}
}
}
}
if (cmi.core.lesson_mode == 'browse') {
if (datamodel[scoid]['cmi.core.lesson_status'].defaultvalue == '' && cmi.core.lesson_status == 'not attempted') {
cmi.core.lesson_status = 'browsed';
}
}
datastring = CollectData(data,'cmi');
datastring += TotalTime();
} else {
datastring = CollectData(data,'cmi');
}
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);
var result = DoRequest(myRequest, datamodelurl, datamodelurlparams + datastring);
if (result === false) {
return false;
}
results = String(result).split('\n');
errorCode = results[1];
return results[0];
}
this.LMSInitialize = LMSInitialize;
this.LMSFinish = LMSFinish;
this.LMSGetValue = LMSGetValue;
this.LMSSetValue = LMSSetValue;
this.LMSCommit = LMSCommit;
this.LMSGetLastError = LMSGetLastError;
this.LMSGetErrorString = LMSGetErrorString;
this.LMSGetDiagnostic = LMSGetDiagnostic;
}
M.scorm_api = {};
M.scorm_api.init = function(Y, def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot,
sesskey, scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride, hidetoc) {
window.API = new SCORMapi1_2(def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot,
sesskey, scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride, hidetoc);
}
+66
View File
@@ -0,0 +1,66 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
require_once($CFG->dirroot.'/mod/scorm/locallib.php');
// Set some vars to use as default values.
$userdata = new stdClass();
$def = new stdClass();
$cmiobj = new stdClass();
$cmiint = new stdClass();
if (!isset($currentorg)) {
$currentorg = '';
}
if ($scoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'sortorder, id')) {
// Drop keys so that it is a simple array.
$scoes = array_values($scoes);
foreach ($scoes as $sco) {
$def->{($sco->id)} = new stdClass();
$userdata->{($sco->id)} = new stdClass();
$def->{($sco->id)} = get_scorm_default($userdata->{($sco->id)}, $scorm, $sco->id, $attempt, $mode);
// Reconstitute objectives.
$cmiobj->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.objectives', array('score'));
$cmiint->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.interactions', array('objectives', 'correct_responses'));
}
}
// If SCORM 1.2 standard mode is disabled allow higher datamodel limits.
if (intval(get_config("scorm", "scormstandard"))) {
$cmistring256 = '^[\\u0000-\\uFFFF]{0,255}$';
$cmistring4096 = '^[\\u0000-\\uFFFF]{0,4096}$';
} else {
$cmistring256 = '^[\\u0000-\\uFFFF]{0,64000}$';
$cmistring4096 = $cmistring256;
}
$scorm->autocommit = ($scorm->autocommit === "1") ? true : false;
$scorm->masteryoverride = ($scorm->masteryoverride === "1") ? true : false;
$PAGE->requires->js_init_call('M.scorm_api.init', array($def, $cmiobj, $cmiint, $cmistring256, $cmistring4096,
scorm_debugging($scorm), $scorm->auto, $scorm->id, $CFG->wwwroot,
sesskey(), $scoid, $attempt, $mode, $id, $currentorg, $scorm->autocommit,
$scorm->masteryoverride, $scorm->hidetoc));
// Pull in the debugging utilities.
if (scorm_debugging($scorm)) {
require_once($CFG->dirroot.'/mod/scorm/datamodels/debug.js.php');
echo html_writer::script('AppendToLog("Moodle SCORM 1.2 API Loaded, Activity: '.
$scorm->name.', SCO: '.$sco->identifier.'", 0);');
}
+106
View File
@@ -0,0 +1,106 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Sets up $userdata array and default values for SCORM 1.2 .
*
* @param stdClass $userdata an empty stdClass variable that should be set up with user values
* @param object $scorm package record
* @param string $scoid SCO Id
* @param string $attempt attempt number for the user
* @param string $mode scorm display mode type
* @return array The default values that should be used for SCORM 1.2 package
*/
function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {
global $USER;
$userdata->student_id = $USER->username;
if (empty(get_config('scorm', 'scormstandard'))) {
$userdata->student_name = fullname($USER);
} else {
$userdata->student_name = $USER->lastname .', '. $USER->firstname;
}
if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {
foreach ($usertrack as $key => $value) {
$userdata->$key = $value;
}
} else {
$userdata->status = '';
$userdata->score_raw = '';
}
if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
foreach ($scodatas as $key => $value) {
$userdata->$key = $value;
}
} else {
throw new \moodle_exception('cannotfindsco', 'scorm');
}
if (!$sco = scorm_get_sco($scoid)) {
throw new \moodle_exception('cannotfindsco', 'scorm');
}
if (isset($userdata->status)) {
if ($userdata->status == '') {
$userdata->entry = 'ab-initio';
} else {
if (isset($userdata->{'cmi.core.exit'}) && ($userdata->{'cmi.core.exit'} == 'suspend')) {
$userdata->entry = 'resume';
} else {
$userdata->entry = '';
}
}
}
$userdata->mode = 'normal';
if (!empty($mode)) {
$userdata->mode = $mode;
}
if ($userdata->mode == 'normal') {
$userdata->credit = 'credit';
} else {
$userdata->credit = 'no-credit';
}
$def = array();
$def['cmi.core.student_id'] = $userdata->student_id;
$def['cmi.core.student_name'] = $userdata->student_name;
$def['cmi.core.credit'] = $userdata->credit;
$def['cmi.core.entry'] = $userdata->entry;
$def['cmi.core.lesson_mode'] = $userdata->mode;
$def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');
$def['cmi.student_data.mastery_score'] = scorm_isset($userdata, 'masteryscore');
$def['cmi.student_data.max_time_allowed'] = scorm_isset($userdata, 'maxtimeallowed');
$def['cmi.student_data.time_limit_action'] = scorm_isset($userdata, 'timelimitaction');
$def['cmi.core.total_time'] = scorm_isset($userdata, 'cmi.core.total_time', '00:00:00');
// Now handle standard userdata items.
$def['cmi.core.lesson_location'] = scorm_isset($userdata, 'cmi.core.lesson_location');
$def['cmi.core.lesson_status'] = scorm_isset($userdata, 'cmi.core.lesson_status');
$def['cmi.core.score.raw'] = scorm_isset($userdata, 'cmi.core.score.raw');
$def['cmi.core.score.max'] = scorm_isset($userdata, 'cmi.core.score.max');
$def['cmi.core.score.min'] = scorm_isset($userdata, 'cmi.core.score.min');
$def['cmi.core.exit'] = scorm_isset($userdata, 'cmi.core.exit');
$def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');
$def['cmi.comments'] = scorm_isset($userdata, 'cmi.comments');
$def['cmi.student_preference.language'] = scorm_isset($userdata, 'cmi.student_preference.language');
$def['cmi.student_preference.audio'] = scorm_isset($userdata, 'cmi.student_preference.audio', '0');
$def['cmi.student_preference.speed'] = scorm_isset($userdata, 'cmi.student_preference.speed', '0');
$def['cmi.student_preference.text'] = scorm_isset($userdata, 'cmi.student_preference.text', '0');
return $def;
}
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
require_once($CFG->dirroot.'/mod/scorm/locallib.php');
$userdata = new stdClass();
$def = new stdClass();
$cmiobj = new stdClass();
$cmiint = new stdClass();
$cmicommentsuser = new stdClass();
$cmicommentslms = new stdClass();
if (!isset($currentorg)) {
$currentorg = '';
}
if ($scoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'sortorder, id')) {
// Drop keys so that it is a simple array.
$scoes = array_values($scoes);
foreach ($scoes as $sco) {
$def->{($sco->id)} = new stdClass();
$userdata->{($sco->id)} = new stdClass();
$def->{($sco->id)} = get_scorm_default($userdata->{($sco->id)}, $scorm, $sco->id, $attempt, $mode);
// Reconstitute objectives, comments_from_learner and comments_from_lms.
$cmiobj->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.objectives', array('score'));
$cmiint->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.interactions', array('objectives', 'correct_responses'));
$cmicommentsuser->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.comments_from_learner', array());
$cmicommentslms->{($sco->id)} = scorm_reconstitute_array_element($scorm->version, $userdata->{($sco->id)},
'cmi.comments_from_lms', array());
}
}
$scorm->autocommit = ($scorm->autocommit === "1") ? true : false;
$PAGE->requires->js_init_call('M.scorm_api.init', array($def, $cmiobj, $cmiint, $cmicommentsuser, $cmicommentslms,
scorm_debugging($scorm), $scorm->auto, $scorm->id, $CFG->wwwroot,
sesskey(), $scoid, $attempt, $mode, $id, $currentorg, $scorm->autocommit));
// Pull in the debugging utilities.
if (scorm_debugging($scorm)) {
require_once($CFG->dirroot.'/mod/scorm/datamodels/debug.js.php');
echo html_writer::script('AppendToLog("Moodle SCORM 1.3 API Loaded, Activity: '.
$scorm->name.', SCO: '.$sco->identifier.'", 0);');
}
File diff suppressed because it is too large Load Diff
+956
View File
@@ -0,0 +1,956 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* functions used by SCORM 1.2/2004 packages.
*
* @package mod_scorm
* @copyright 1999 onwards Roberto Pinna
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function scorm_get_resources($blocks) {
$resources = array();
foreach ($blocks as $block) {
if ($block['name'] == 'RESOURCES' && isset($block['children'])) {
foreach ($block['children'] as $resource) {
if ($resource['name'] == 'RESOURCE') {
$resources[addslashes_js($resource['attrs']['IDENTIFIER'])] = $resource['attrs'];
}
}
}
}
return $resources;
}
function scorm_get_manifest($blocks, $scoes) {
global $OUTPUT;
static $parents = array();
static $resources;
static $manifest;
static $organization;
$manifestresourcesnotfound = array();
if (count($blocks) > 0) {
foreach ($blocks as $block) {
switch ($block['name']) {
case 'METADATA':
if (isset($block['children'])) {
foreach ($block['children'] as $metadata) {
if ($metadata['name'] == 'SCHEMAVERSION') {
if (empty($scoes->version)) {
$isversionset = (preg_match("/^(1\.2)$|^(CAM )?(1\.3)$/", $metadata['tagData'], $matches));
if (isset($metadata['tagData']) && $isversionset) {
$scoes->version = 'SCORM_'.$matches[count($matches) - 1];
} else {
$isversionset = (preg_match("/^2004 (3rd|4th) Edition$/", $metadata['tagData'], $matches));
if (isset($metadata['tagData']) && $isversionset) {
$scoes->version = 'SCORM_1.3';
} else {
$scoes->version = 'SCORM_1.2';
}
}
}
}
}
}
break;
case 'MANIFEST':
$manifest = $block['attrs']['IDENTIFIER'];
$organization = '';
$resources = array();
$resources = scorm_get_resources($block['children']);
$scoes = scorm_get_manifest($block['children'], $scoes);
if (empty($scoes->elements) || count($scoes->elements) <= 0) {
foreach ($resources as $item => $resource) {
if (!empty($resource['HREF'])) {
$sco = new stdClass();
$sco->identifier = $item;
$sco->title = $item;
$sco->parent = '/';
$sco->launch = $resource['HREF'];
$sco->scormtype = $resource['ADLCP:SCORMTYPE'];
$scoes->elements[$manifest][$organization][$item] = $sco;
}
}
}
break;
case 'ORGANIZATIONS':
if (!isset($scoes->defaultorg) && isset($block['attrs']['DEFAULT'])) {
$scoes->defaultorg = $block['attrs']['DEFAULT'];
}
if (!empty($block['children'])) {
$scoes = scorm_get_manifest($block['children'], $scoes);
}
break;
case 'ORGANIZATION':
$identifier = $block['attrs']['IDENTIFIER'];
$organization = '';
$scoes->elements[$manifest][$organization][$identifier] = new stdClass();
$scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier;
$scoes->elements[$manifest][$organization][$identifier]->parent = '/';
$scoes->elements[$manifest][$organization][$identifier]->launch = '';
$scoes->elements[$manifest][$organization][$identifier]->scormtype = '';
$parents = array();
$parent = new stdClass();
$parent->identifier = $identifier;
$parent->organization = $organization;
array_push($parents, $parent);
$organization = $identifier;
if (!empty($block['children'])) {
$scoes = scorm_get_manifest($block['children'], $scoes);
}
array_pop($parents);
break;
case 'ITEM':
$parent = array_pop($parents);
array_push($parents, $parent);
$identifier = $block['attrs']['IDENTIFIER'];
$scoes->elements[$manifest][$organization][$identifier] = new stdClass();
$scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier;
$scoes->elements[$manifest][$organization][$identifier]->parent = $parent->identifier;
if (!isset($block['attrs']['ISVISIBLE'])) {
$block['attrs']['ISVISIBLE'] = 'true';
}
$scoes->elements[$manifest][$organization][$identifier]->isvisible = $block['attrs']['ISVISIBLE'];
if (!isset($block['attrs']['PARAMETERS'])) {
$block['attrs']['PARAMETERS'] = '';
}
$scoes->elements[$manifest][$organization][$identifier]->parameters = $block['attrs']['PARAMETERS'];
if (!isset($block['attrs']['IDENTIFIERREF'])) {
$scoes->elements[$manifest][$organization][$identifier]->launch = '';
$scoes->elements[$manifest][$organization][$identifier]->scormtype = 'asset';
} else {
$idref = addslashes_js($block['attrs']['IDENTIFIERREF']);
$base = '';
if (isset($resources[$idref]['XML:BASE'])) {
$base = $resources[$idref]['XML:BASE'];
}
if (!isset($resources[$idref])) {
$manifestresourcesnotfound[] = $idref;
$scoes->elements[$manifest][$organization][$identifier]->launch = '';
} else {
$scoes->elements[$manifest][$organization][$identifier]->launch = $base.$resources[$idref]['HREF'];
if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) {
$resources[$idref]['ADLCP:SCORMTYPE'] = 'asset';
}
$scoes->elements[$manifest][$organization][$identifier]->scormtype = $resources[$idref]['ADLCP:SCORMTYPE'];
}
}
$parent = new stdClass();
$parent->identifier = $identifier;
$parent->organization = $organization;
array_push($parents, $parent);
if (!empty($block['children'])) {
$scoes = scorm_get_manifest($block['children'], $scoes);
}
array_pop($parents);
break;
case 'TITLE':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->title = $block['tagData'];
break;
case 'ADLCP:PREREQUISITES':
if ($block['attrs']['TYPE'] == 'aicc_script') {
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->prerequisites = $block['tagData'];
}
break;
case 'ADLCP:MAXTIMEALLOWED':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->maxtimeallowed = $block['tagData'];
break;
case 'ADLCP:TIMELIMITACTION':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->timelimitaction = $block['tagData'];
break;
case 'ADLCP:DATAFROMLMS':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->datafromlms = $block['tagData'];
break;
case 'ADLCP:MASTERYSCORE':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['tagData'])) {
$block['tagData'] = '';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->masteryscore = $block['tagData'];
break;
case 'ADLCP:COMPLETIONTHRESHOLD':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!isset($block['attrs']['MINPROGRESSMEASURE'])) {
$block['attrs']['MINPROGRESSMEASURE'] = '1.0';
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->threshold = $block['attrs']['MINPROGRESSMEASURE'];
break;
case 'ADLNAV:PRESENTATION':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!empty($block['children'])) {
foreach ($block['children'] as $adlnav) {
if ($adlnav['name'] == 'ADLNAV:NAVIGATIONINTERFACE') {
foreach ($adlnav['children'] as $adlnavinterface) {
if ($adlnavinterface['name'] == 'ADLNAV:HIDELMSUI') {
if ($adlnavinterface['tagData'] == 'continue') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hidecontinue = 1;
}
if ($adlnavinterface['tagData'] == 'previous') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideprevious = 1;
}
if ($adlnavinterface['tagData'] == 'exit') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideexit = 1;
}
if ($adlnavinterface['tagData'] == 'exitAll') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideexitall = 1;
}
if ($adlnavinterface['tagData'] == 'abandon') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideabandon = 1;
}
if ($adlnavinterface['tagData'] == 'abandonAll') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideabandonall = 1;
}
if ($adlnavinterface['tagData'] == 'suspendAll') {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->hidesuspendall = 1;
}
}
}
}
}
}
break;
case 'IMSSS:SEQUENCING':
$parent = array_pop($parents);
array_push($parents, $parent);
if (!empty($block['children'])) {
foreach ($block['children'] as $sequencing) {
if ($sequencing['name'] == 'IMSSS:CONTROLMODE') {
if (isset($sequencing['attrs']['CHOICE'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->choice =
$sequencing['attrs']['CHOICE'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['CHOICEEXIT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->choiceexit =
$sequencing['attrs']['CHOICEEXIT'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['FLOW'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->flow =
$sequencing['attrs']['FLOW'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['FORWARDONLY'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->forwardonly =
$sequencing['attrs']['FORWARDONLY'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['USECURRENTATTEMPTOBJECTINFO'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptobjectinfo =
$sequencing['attrs']['USECURRENTATTEMPTOBJECTINFO'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['USECURRENTATTEMPTPROGRESSINFO'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptprogressinfo =
$sequencing['attrs']['USECURRENTATTEMPTPROGRESSINFO'] == 'true' ? 1 : 0;
}
}
if ($sequencing['name'] == 'IMSSS:DELIVERYCONTROLS') {
if (isset($sequencing['attrs']['TRACKED'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->tracked =
$sequencing['attrs']['TRACKED'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['COMPLETIONSETBYCONTENT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->completionsetbycontent =
$sequencing['attrs']['COMPLETIONSETBYCONTENT'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['OBJECTIVESETBYCONTENT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectivesetbycontent =
$sequencing['attrs']['OBJECTIVESETBYCONTENT'] == 'true' ? 1 : 0;
}
}
if ($sequencing['name'] == 'ADLSEQ:CONSTRAINEDCHOICECONSIDERATIONS') {
if (isset($sequencing['attrs']['CONSTRAINCHOICE'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->constrainChoice =
$sequencing['attrs']['CONSTRAINCHOICE'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['PREVENTACTIVATION'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->preventactivation =
$sequencing['attrs']['PREVENTACTIVATION'] == 'true' ? 1 : 0;
}
}
if ($sequencing['name'] == 'IMSSS:OBJECTIVES') {
$objectives = array();
foreach ($sequencing['children'] as $objective) {
$objectivedata = new stdClass();
$objectivedata->primaryobj = 0;
switch ($objective['name']) {
case 'IMSSS:PRIMARYOBJECTIVE':
$objectivedata->primaryobj = 1;
case 'IMSSS:OBJECTIVE':
$objectivedata->satisfiedbymeasure = 0;
if (isset($objective['attrs']['SATISFIEDBYMEASURE'])) {
$objectivedata->satisfiedbymeasure =
$objective['attrs']['SATISFIEDBYMEASURE'] == 'true' ? 1 : 0;
}
$objectivedata->objectiveid = '';
if (isset($objective['attrs']['OBJECTIVEID'])) {
$objectivedata->objectiveid = $objective['attrs']['OBJECTIVEID'];
}
$objectivedata->minnormalizedmeasure = 1.0;
if (!empty($objective['children'])) {
$mapinfos = array();
foreach ($objective['children'] as $objectiveparam) {
if ($objectiveparam['name'] == 'IMSSS:MINNORMALIZEDMEASURE') {
if (isset($objectiveparam['tagData'])) {
$objectivedata->minnormalizedmeasure = $objectiveparam['tagData'];
} else {
$objectivedata->minnormalizedmeasure = 0;
}
}
if ($objectiveparam['name'] == 'IMSSS:MAPINFO') {
$mapinfo = new stdClass();
$mapinfo->targetobjectiveid = '';
if (isset($objectiveparam['attrs']['TARGETOBJECTIVEID'])) {
$mapinfo->targetobjectiveid =
$objectiveparam['attrs']['TARGETOBJECTIVEID'];
}
$mapinfo->readsatisfiedstatus = 1;
if (isset($objectiveparam['attrs']['READSATISFIEDSTATUS'])) {
$mapinfo->readsatisfiedstatus =
$objectiveparam['attrs']['READSATISFIEDSTATUS'] == 'true' ? 1 : 0;
}
$mapinfo->writesatisfiedstatus = 0;
if (isset($objectiveparam['attrs']['WRITESATISFIEDSTATUS'])) {
$mapinfo->writesatisfiedstatus =
$objectiveparam['attrs']['WRITESATISFIEDSTATUS'] == 'true' ? 1 : 0;
}
$mapinfo->readnormalizemeasure = 1;
if (isset($objectiveparam['attrs']['READNORMALIZEDMEASURE'])) {
$mapinfo->readnormalizemeasure =
$objectiveparam['attrs']['READNORMALIZEDMEASURE'] == 'true' ? 1 : 0;
}
$mapinfo->writenormalizemeasure = 0;
if (isset($objectiveparam['attrs']['WRITENORMALIZEDMEASURE'])) {
$mapinfo->writenormalizemeasure =
$objectiveparam['attrs']['WRITENORMALIZEDMEASURE'] == 'true' ? 1 : 0;
}
array_push($mapinfos, $mapinfo);
}
}
if (!empty($mapinfos)) {
$objectivesdata->mapinfos = $mapinfos;
}
}
break;
}
array_push($objectives, $objectivedata);
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectives = $objectives;
}
if ($sequencing['name'] == 'IMSSS:LIMITCONDITIONS') {
if (isset($sequencing['attrs']['ATTEMPTLIMIT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptLimit =
$sequencing['attrs']['ATTEMPTLIMIT'];
}
if (isset($sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptAbsoluteDurationLimit =
$sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT'];
}
}
if ($sequencing['name'] == 'IMSSS:ROLLUPRULES') {
if (isset($sequencing['attrs']['ROLLUPOBJECTIVESATISFIED'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupobjectivesatisfied =
$sequencing['attrs']['ROLLUPOBJECTIVESATISFIED'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['ROLLUPPROGRESSCOMPLETION'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupprogresscompletion =
$sequencing['attrs']['ROLLUPPROGRESSCOMPLETION'] == 'true' ? 1 : 0;
}
if (isset($sequencing['attrs']['OBJECTIVEMEASUREWEIGHT'])) {
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectivemeasureweight =
$sequencing['attrs']['OBJECTIVEMEASUREWEIGHT'];
}
if (!empty($sequencing['children'])) {
$rolluprules = array();
foreach ($sequencing['children'] as $sequencingrolluprule) {
if ($sequencingrolluprule['name'] == 'IMSSS:ROLLUPRULE' ) {
$rolluprule = new stdClass();
$rolluprule->childactivityset = 'all';
if (isset($sequencingrolluprule['attrs']['CHILDACTIVITYSET'])) {
$rolluprule->childactivityset = $sequencingrolluprule['attrs']['CHILDACTIVITYSET'];
}
$rolluprule->minimumcount = 0;
if (isset($sequencingrolluprule['attrs']['MINIMUMCOUNT'])) {
$rolluprule->minimumcount = $sequencingrolluprule['attrs']['MINIMUMCOUNT'];
}
$rolluprule->minimumpercent = 0.0000;
if (isset($sequencingrolluprule['attrs']['MINIMUMPERCENT'])) {
$rolluprule->minimumpercent = $sequencingrolluprule['attrs']['MINIMUMPERCENT'];
}
if (!empty($sequencingrolluprule['children'])) {
foreach ($sequencingrolluprule['children'] as $rolluproleconditions) {
if ($rolluproleconditions['name'] == 'IMSSS:ROLLUPCONDITIONS') {
$conditions = array();
$rolluprule->conditioncombination = 'all';
if (isset($rolluproleconditions['attrs']['CONDITIONCOMBINATION'])) {
$rolluprule->CONDITIONCOMBINATION = $rolluproleconditions['attrs']['CONDITIONCOMBINATION'];
}
foreach ($rolluproleconditions['children'] as $rolluprulecondition) {
if ($rolluprulecondition['name'] == 'IMSSS:ROLLUPCONDITION') {
$condition = new stdClass();
if (isset($rolluprulecondition['attrs']['CONDITION'])) {
$condition->cond = $rolluprulecondition['attrs']['CONDITION'];
}
$condition->operator = 'noOp';
if (isset($rolluprulecondition['attrs']['OPERATOR'])) {
$condition->operator = $rolluprulecondition['attrs']['OPERATOR'];
}
array_push($conditions, $condition);
}
}
$rolluprule->conditions = $conditions;
}
if ($rolluproleconditions['name'] == 'IMSSS:ROLLUPACTION') {
$rolluprule->rollupruleaction = $rolluproleconditions['attrs']['ACTION'];
}
}
}
array_push($rolluprules, $rolluprule);
}
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->rolluprules = $rolluprules;
}
}
if ($sequencing['name'] == 'IMSSS:SEQUENCINGRULES') {
if (!empty($sequencing['children'])) {
$sequencingrules = array();
foreach ($sequencing['children'] as $conditionrules) {
$conditiontype = -1;
switch($conditionrules['name']) {
case 'IMSSS:PRECONDITIONRULE':
$conditiontype = 0;
break;
case 'IMSSS:POSTCONDITIONRULE':
$conditiontype = 1;
break;
case 'IMSSS:EXITCONDITIONRULE':
$conditiontype = 2;
break;
}
if (!empty($conditionrules['children'])) {
$sequencingrule = new stdClass();
foreach ($conditionrules['children'] as $conditionrule) {
if ($conditionrule['name'] == 'IMSSS:RULECONDITIONS') {
$ruleconditions = array();
$sequencingrule->conditioncombination = 'all';
if (isset($conditionrule['attrs']['CONDITIONCOMBINATION'])) {
$sequencingrule->conditioncombination = $conditionrule['attrs']['CONDITIONCOMBINATION'];
}
foreach ($conditionrule['children'] as $rulecondition) {
if ($rulecondition['name'] == 'IMSSS:RULECONDITION') {
$condition = new stdClass();
if (isset($rulecondition['attrs']['CONDITION'])) {
$condition->cond = $rulecondition['attrs']['CONDITION'];
}
$condition->operator = 'noOp';
if (isset($rulecondition['attrs']['OPERATOR'])) {
$condition->operator = $rulecondition['attrs']['OPERATOR'];
}
$condition->measurethreshold = 0.0000;
if (isset($rulecondition['attrs']['MEASURETHRESHOLD'])) {
$condition->measurethreshold = $rulecondition['attrs']['MEASURETHRESHOLD'];
}
$condition->referencedobjective = '';
if (isset($rulecondition['attrs']['REFERENCEDOBJECTIVE'])) {
$condition->referencedobjective = $rulecondition['attrs']['REFERENCEDOBJECTIVE'];
}
array_push($ruleconditions, $condition);
}
}
$sequencingrule->ruleconditions = $ruleconditions;
}
if ($conditionrule['name'] == 'IMSSS:RULEACTION') {
$sequencingrule->action = $conditionrule['attrs']['ACTION'];
}
$sequencingrule->type = $conditiontype;
}
array_push($sequencingrules, $sequencingrule);
}
}
$scoes->elements[$manifest][$parent->organization][$parent->identifier]->sequencingrules = $sequencingrules;
}
}
}
}
break;
}
}
}
if (!empty($manifestresourcesnotfound)) {
// Throw warning to user to let them know manifest contains references to resources that don't appear to exist.
if (!defined('DEBUGGING_PRINTED')) {
// Prevent redirect and display warning.
define('DEBUGGING_PRINTED', 1);
}
echo $OUTPUT->notification(get_string('invalidmanifestresource', 'scorm').' '. implode(', ', $manifestresourcesnotfound));
}
return $scoes;
}
/**
* Sets up SCORM 1.2/2004 packages using the manifest file.
* Called whenever SCORM changes
* @param object $scorm instance - fields are updated and changes saved into database
* @param stored_file|string $manifest - path to manifest file or stored_file.
* @return bool
*/
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);
}
$defaultorgid = 0;
$firstinorg = 0;
$pattern = '/&(?!\w{2,6};)/';
$replacement = '&amp;';
$xmltext = preg_replace($pattern, $replacement, $xmltext);
$objxml = new xml2Array();
$manifests = $objxml->parse($xmltext);
$scoes = new stdClass();
$scoes->version = '';
$scoes = scorm_get_manifest($manifests, $scoes);
$newscoes = array();
$sortorder = 0;
if (!empty($scoes->elements) && is_iterable($scoes->elements)) {
$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) {
$sortorder++;
// This new db mngt will support all SCORM future extensions.
$newitem = new stdClass();
$newitem->scorm = $scorm->id;
$newitem->manifest = $manifest;
$newitem->organization = $organization;
$newitem->sortorder = $sortorder;
$standarddatas = array('parent', 'identifier', 'launch', 'scormtype', 'title');
foreach ($standarddatas as $standarddata) {
if (isset($item->$standarddata)) {
$newitem->$standarddata = $item->$standarddata;
} else {
$newitem->$standarddata = '';
}
}
if (!empty($defaultorgid) && !empty($scoes->defaultorg) && empty($firstinorg) &&
$newitem->parent == $scoes->defaultorg) {
$firstinorg = $sortorder;
}
if (!empty($olditems) && ($olditemid = scorm_array_search('identifier', $newitem->identifier, $olditems))) {
$newitem->id = $olditemid;
// Update the Sco sortorder but keep id so that user tracks are kept against the same ids.
$DB->update_record('scorm_scoes', $newitem);
$id = $olditemid;
// Remove all old data so we don't duplicate it.
$DB->delete_records('scorm_scoes_data', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_objective', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_mapinfo', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_ruleconds', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_rulecond', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_rolluprule', array('scoid' => $olditemid));
$DB->delete_records('scorm_seq_rolluprulecond', array('scoid' => $olditemid));
// Now remove this SCO from the olditems object as we have dealt with it.
unset($olditems[$olditemid]);
} else {
// Insert the new SCO, and retain the link between the old and new for later adjustment.
$id = $DB->insert_record('scorm_scoes', $newitem);
}
// Save this sco in memory so we can use it later.
$newscoes[$id] = $newitem;
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->operator = $rulecondition->operator;
$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;
$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 = trim($objective->minnormalizedmeasure);
$objectiveid = $DB->insert_record('scorm_seq_objective', $obj);
if (isset($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);
}
}
}
}
if (empty($defaultorgid) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) {
$defaultorgid = $id;
}
}
}
}
if (!empty($olditems)) {
foreach ($olditems as $olditem) {
$DB->delete_records('scorm_scoes', ['id' => $olditem->id]);
$DB->delete_records('scorm_scoes_data', ['scoid' => $olditem->id]);
scorm_delete_tracks($scorm->id, $olditem->id);
$DB->delete_records('scorm_seq_objective', ['scoid' => $olditem->id]);
$DB->delete_records('scorm_seq_mapinfo', ['scoid' => $olditem->id]);
$DB->delete_records('scorm_seq_ruleconds', ['scoid' => $olditem->id]);
$DB->delete_records('scorm_seq_rulecond', ['scoid' => $olditem->id]);
$DB->delete_records('scorm_seq_rolluprule', ['scoid' => $olditem->id]);
$DB->delete_records('scorm_seq_rolluprulecond', ['scoid' => $olditem->id]);
}
}
if (empty($scoes->version)) {
$scoes->version = 'SCORM_1.2';
}
$DB->set_field('scorm', 'version', $scoes->version, array('id' => $scorm->id));
$scorm->version = $scoes->version;
}
$scorm->launch = 0;
// Check launch sco is valid.
if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && !empty($newscoes[$defaultorgid]->launch)) {
// Launch param is valid - do nothing.
$scorm->launch = $defaultorgid;
} else if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && empty($newscoes[$defaultorgid]->launch)) {
// The launch is probably the default org so we need to find the first launchable item inside this org.
$sqlselect = 'scorm = ? AND sortorder >= ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
// We use get_records here as we need to pass a limit in the query that works cross db.
$scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id, $firstinorg), 'sortorder', 'id', 0, 1);
if (!empty($scoes)) {
$sco = reset($scoes); // We only care about the first record - the above query only returns one.
$scorm->launch = $sco->id;
}
}
if (empty($scorm->launch)) {
// No valid Launch is specified - find the first launchable sco instead.
$sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
// We use get_records here as we need to pass a limit in the query that works cross db.
$scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1);
if (!empty($scoes)) {
$sco = reset($scoes); // We only care about the first record - the above query only returns one.
$scorm->launch = $sco->id;
}
}
return true;
}
function scorm_optionals_data($item, $standarddata) {
$result = array();
$sequencingdata = array('sequencingrules', 'rolluprules', 'objectives');
foreach ($item as $element => $value) {
if (! in_array($element, $standarddata)) {
if (! in_array($element, $sequencingdata)) {
$result[] = $element;
}
}
}
return $result;
}
function scorm_is_leaf($sco) {
global $DB;
if ($DB->record_exists('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier))) {
return false;
}
return true;
}
function scorm_get_parent($sco) {
global $DB;
if ($sco->parent != '/') {
if ($parent = $DB->get_record('scorm_scoes', array('scorm' => $sco->scorm, 'identifier' => $sco->parent))) {
return scorm_get_sco($parent->id);
}
}
return null;
}
function scorm_get_children($sco) {
global $DB;
$children = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier), 'sortorder, id');
if (!empty($children)) {
return $children;
}
return null;
}
function scorm_get_available_children($sco) {
global $DB;
$res = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier), 'sortorder, id');
if (!$res || $res == null) {
return false;
} else {
foreach ($res as $sco) {
$result[] = $sco;
}
return $result;
}
}
function scorm_get_available_descendent($descend, $sco) {
if ($sco == null) {
return $descend;
} else {
$avchildren = scorm_get_available_children($sco);
foreach ($avchildren as $avchild) {
array_push($descend, $avchild);
}
foreach ($avchildren as $avchild) {
scorm_get_available_descendent($descend, $avchild);
}
}
}
function scorm_get_siblings($sco) {
global $DB;
if ($siblings = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->parent), 'sortorder, id')) {
unset($siblings[$sco->id]);
if (!empty($siblings)) {
return $siblings;
}
}
return null;
}
// Get an array that contains all the parent scos for this sco.
function scorm_get_ancestors($sco) {
$ancestors = array();
$continue = true;
while ($continue) {
$ancestor = scorm_get_parent($sco);
if (!empty($ancestor) && $ancestor->id !== $sco->id) {
$sco = $ancestor;
$ancestors[] = $ancestor;
if ($sco->parent == '/') {
$continue = false;
}
} else {
$continue = false;
}
}
return $ancestors;
}
function scorm_get_preorder(&$preorder = array(), $sco = null) {
if ($sco != null) {
array_push($preorder, $sco);
if ($children = scorm_get_children($sco)) {
foreach ($children as $child) {
scorm_get_preorder($preorder, $child);
}
}
}
return $preorder;
}
function scorm_find_common_ancestor($ancestors, $sco) {
$pos = scorm_array_search('identifier', $sco->parent, $ancestors);
if ($sco->parent != '/') {
if ($pos === false) {
return scorm_find_common_ancestor($ancestors, scorm_get_parent($sco));
}
}
return $pos;
}
/* Usage
Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML;
$objXML = new xml2Array();
$arroutput = $objXML->parse($strYourXML);
print_r($arroutput); //print it out, or do whatever!
*/
class xml2Array {
public $arroutput = array();
public $resparser;
public $strxmldata;
/**
* Convert a utf-8 string to html entities
*
* @param string $str The UTF-8 string
* @return string
*/
public function utf8_to_entities($str) {
global $CFG;
$entities = '';
$values = array();
$lookingfor = 1;
return $str;
}
/**
* Parse an XML text string and create an array tree that rapresent the XML structure
*
* @param string $strinputxml The XML string
* @return array
*/
public function parse($strinputxml) {
$this->resparser = xml_parser_create ('UTF-8');
xml_set_object($this->resparser, $this);
xml_set_element_handler($this->resparser, "tagopen", "tagclosed");
xml_set_character_data_handler($this->resparser, "tagdata");
$this->strxmldata = xml_parse($this->resparser, $strinputxml );
if (!$this->strxmldata) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->resparser)),
xml_get_current_line_number($this->resparser)));
}
xml_parser_free($this->resparser);
return $this->arroutput;
}
public function tagopen($parser, $name, $attrs) {
$tag = array("name" => $name, "attrs" => $attrs);
array_push($this->arroutput, $tag);
}
public function tagdata($parser, $tagdata) {
if (trim($tagdata)) {
if (isset($this->arroutput[count($this->arroutput) - 1]['tagData'])) {
$this->arroutput[count($this->arroutput) - 1]['tagData'] .= $this->utf8_to_entities($tagdata);
} else {
$this->arroutput[count($this->arroutput) - 1]['tagData'] = $this->utf8_to_entities($tagdata);
}
}
}
public function tagclosed($parser, $name) {
$this->arroutput[count($this->arroutput) - 2]['children'][] = $this->arroutput[count($this->arroutput) - 1];
array_pop($this->arroutput);
}
}
@@ -0,0 +1,63 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
require_once('../../../config.php');
require_once($CFG->dirroot.'/mod/scorm/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.
$attempt = required_param('attempt', PARAM_INT); // Attempt number.
$function = required_param('function', PARAM_RAW); // Function to call.
$request = optional_param('request', '', PARAM_RAW); // Scorm ID.
if (!empty($id)) {
$cm = get_coursemodule_from_id('scorm', $id, 0, false, MUST_EXIST);
$course = $DB->get_record("course", array("id" => $cm->course), '*', MUST_EXIST);
$scorm = $DB->get_record("scorm", array("id" => $cm->instance), '*', MUST_EXIST);
} else if (!empty($a)) {
$scorm = $DB->get_record("scorm", array("id" => $a), '*', MUST_EXIST);
$course = $DB->get_record("course", array("id" => $scorm->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance("scorm", $scorm->id, $course->id, false, MUST_EXIST);
} else {
throw new \moodle_exception('missingparameter');
}
$PAGE->set_url('/mod/scorm/datamodels/sequencinghandler.php',
array('scoid' => $scoid, 'attempt' => $attempt, 'id' => $cm->id, 'function' => $function, 'request' => $request));
require_login($course, false, $cm);
if (!empty($scoid) && !empty($function)) {
require_once($CFG->dirroot.'/mod/scorm/datamodels/scorm_13lib.php');
if (has_capability('mod/scorm:savetrack', context_module::instance($cm->id))) {
$result = null;
switch ($function) {
case 'scorm_seq_flow' :
if ($request == 'forward' || $request == 'backward') {
$seq = scorm_seq_navigation ($scoid, $USER->id, $request.'_', $attempt);
$sco = scorm_get_sco($scoid);
$seq = scorm_seq_flow($sco, $request, $seq, true, $USER->id);
if (!empty($seq->nextactivity)) {
scorm_seq_end_attempt($sco, $USER->id, $seq);
}
}
echo json_encode($seq);
break;
}
}
}
File diff suppressed because it is too large Load Diff