"Superuser" Feature

May 16, 2022

*When I tried to submit a feature request the page blanks as soon as I select storyline 360*

 

It would be great if we could get a superuser feature where I as the course creator can either enter a password, or key combo that allows me to navigate the course. 

 

For example, on several courses I have either the player controls or added buttons hidden until triggered. However, for whatever reason, some users will get stuck, despite working out all variables (same machine, same browser, same LMS, cookies cleared, etc.). In other words, there's no rhyme or reason that 1 user can complete the course no issue, and then another user has an unreproducible error that doesn't let them continue. 

 

I can, of course, build in redundancy, but there's no guarantee that works, no guarantee the user can't exploit it, and it takes a lot of time. Ideally, it would be nice if as the course creator, I could use a key combo, or otherwise take over the course, proceed to the next slide, and then reset the controls so the user can continue and complete the course without needed to restart.

13 Replies
Thaddeus Ashcliffe

I created a simple example where the user can enter the Komani Code (Up, Up, Down,Down, Left, Right, Left, Right, A, B, enter) to activate a trigger.

I used a variable that would increment if a key was pressed, but only if the variable had progressed with in a range or was = to a number.  This keeps the user from hitting the same key over and over. 
Granted this approach only cares if the sequence is eventually entered, it ignores anything else.  Odds of someone hitting shuch a combo is very rare.

Jeff R

Thank you for the very helpful example.

For context, some courses sporadically have an error that does not allow the user to progress (i.e. a variable is met where a value >/=4 changes the state of the next button to normal, however, despite the variable being 4 or greater, the next button does not change). 

In my case, users are required to complete x hours for certification purposes, so most slides have requirements that must be met to proceed without skipping in order to fulfill the time requirement. If a user is 30 minutes into an hour long class, and the aforementioned error occurs, then my only recourse is to have them start over from the beginning-- which is of course, not desirable. 

In the Komani code example, I'd have to implement this on every slide in addition to the other triggers that are there-- which could work, but definitely becomes time consuming. 

The scenario I envision is being able to remote into a user's PC, somehow either change the state of objects, or possibly show the course menu, but then return the course to usermode so the user can continue the course without restarting it, after resolving the error. 

The issue I have is that while I can remote into a user's PC and observe the error, it is sporadic, and I've yet to be able to reproduce an error; but it happens often enough that it is frustrating. 

Phil Mayor

This could be built on a slide master so would then cover the whole course. Not sure I would want this feature built-in. IMHO most errors are with the course/developer rather than the LMS, once open a course is typically sandboxes from the LMS apart from sending data. This would introduce a back door that could be exploited.

Sent from my iPhone

Thaddeus Ashcliffe

If you are encountering roadblocks it might be useful to have a spare scene/slide/layer where in you have every navigation variable listed.  that way you can access this to check that course is working correctly.  This is what i'd use the cheat code to access.

This sounds like a trigger issue, making some users superuser's doesn't seem like it would help in this regard.

Jeff R

I didn't think about doing that on a slide master, but yes, you are correct. That's where my frustration is, as I can not repeat any of these errors that don't allow the user to proceed, however, I can observe them occurring. Articulate has essentially told me in the past that it is an LMS issue, which I have no control over, when I've asked for help. 

Phil Mayor

This is the code I use

//Opens a Storyline variable debugger window

//Add this JavaScript to some Storyline trigger (ex: when user presses F9 in the slide master)

 

"use strict";

var pl = GetPlayer();

var debugWin = window.open(undefined, "debug", "width=600,height=800,toolbar=no,location=no");

var debugDoc = debugWin.document;

debugDoc.body.innerHTML = "";

debugDoc.title = "Storyline Debugger";

var refreshButton = debugDoc.createElement("INPUT");

refreshButton.value = "Refresh";

refreshButton.type = "button";

refreshButton.addEventListener("click", refreshVars);

debugDoc.body.appendChild(refreshButton);

var tableElem = debugDoc.createElement("TABLE");

debugDoc.body.appendChild(tableElem);

getStorylineUserVariables(function (slVars) {

 slVars.sort(function(x, y){return x.name > y.name;});

 for (var a = 0; a < slVars.length; a++) {

 var v = slVars[a];

 var trElem = debugDoc.createElement("TR");

 if (a % 2 == 1) trElem.style.backgroundColor = "#dddddd";

 var tdElem = debugDoc.createElement("TD");

 tdElem.innerHTML = v.name;

 tdElem.style.padding = "10px 2px";

 trElem.appendChild(tdElem);

 var inputElem = debugDoc.createElement("INPUT");

 inputElem.storylineVarName = v.name;

 if (v.type == "number") {

 inputElem.type = "number";

 inputElem.step = undefined;

 inputElem.value = pl.GetVar(v.name);

 } else if (v.type == "boolean") {

 inputElem.type = "checkbox";

 inputElem.checked = pl.GetVar(v.name);

 } else {

 inputElem.type = "text";

 inputElem.value = pl.GetVar(v.name);

 }

 tdElem = debugDoc.createElement("TD");

 tdElem.appendChild(inputElem);

 trElem.appendChild(tdElem);

 tableElem.appendChild(trElem);

 }

} );

tableElem.addEventListener("change", function (e) {

 var inputElem = e.target;

 var slVarName = inputElem.storylineVarName;

 var newVal = "";

 if (inputElem.getAttribute("type") == "checkbox") {

 newVal = inputElem.checked;

 } else {

 newVal = inputElem.value;

 }

 pl.SetVar(slVarName, newVal);

} );

 

function refreshVars () {

 var inputs = tableElem.querySelectorAll("input");

 for (var a = 0; a < inputs.length; a++) {

 var elem = inputs[a];

 var val = pl.GetVar(elem.storylineVarName);

 if (elem.type == "checkbox") {

 elem.checked = val;

 } else {

 elem.value = val;

 }

 }

}

 

function getStorylineUserVariables(callback) {

 //Overide Storyline's globalProvideData function temporarily

 var origGlobalProvideData = window.globalProvideData;

 var scriptElem = document.createElement("SCRIPT");

 scriptElem.src = "html5/data/js/data.js";

 window.globalProvideData = function (foo, data) {

 var dataObj = JSON.parse(data);

 var variableList = [];

 for (var a = 0; a < dataObj.variables.length; a++) {

 var varObj = {name: dataObj.variables[a].name, type: dataObj.variables[a].type, defaultValue: dataObj.variables[a].value};

 if (varObj.name.match(/^[0-9a-zA-Z]{11}_RetryModeInteractionIncompleteOnLoad$/) == null && 

 varObj.name.match(/^LastSlideViewed_[0-9a-zA-Z]{11}$/) == null && 

 varObj.name.match(/^PrintPromptUsername_[0-9a-zA-Z]{11}$/) == null &&

 varObj.name.match(/^PrintPromptWindowQuizVar_[0-9a-zA-Z]{11}$/) == null){

 

 variableList.push(varObj);

 }

 }

 document.body.removeChild(scriptElem);

 window.globalProvideData = origGlobalProvideData;

 callback(variableList);

 }

 document.body.appendChild(scriptElem);

}