Forum Discussion
Custom resume functionality Storyline
Hello,
I’m looking for a way to store progress data so that learners can resume a web-exported Storyline course based on their user information.
Background:
I have a custom application where users log in with credentials from a database. Once logged in, they can access several Storyline courses published for the web. When a user completes a course, I write a record to the database using a custom JavaScript call to a PHP script on the last slide. This allows me to show in the application whether a learner has finished the course.
However, since my application is not an LMS, I can’t use SCORM or other e-learning standards to capture learning data. Now there’s a need to allow learners to resume a course, for example, if they stop halfway through. The community suggested I ask this question here, so I hope someone can point me in the right direction.
Thank you in advance for your help!
3 Replies
- SamHillSuper Hero
Hi Miguelvanden090 since it sounds like you know your way around JavaScript and PHP and writing to databases, I think the simplest way for you to achieve this level of tracking is to leverage the session data that Storyline will store in the users browser, and store that data in the database. You would of course have to ensure that that data is available to the course when it is launched again (as it will attempt to get cookie data) so maybe you could write the appropriate cookie, before the content attempts to retrieve it, so it is available for the course when launched again. That way, the cookie data will be associated with the current user. I've never tried this approach though.
The approach I've used in the past is to publish as SCORM 1.2, and build yourself a pseudo SCORM API. You would not need to hook up the entire API to correct data, you can just return expected codes to the content to pretend whatever it tried to do with the API was successful, but when it comes to suspend_data, the data could be written to the database and could also be made available when the user next launches the course.
I'm not sure what your JavaScript/PHP skill level is like. I built something like this for a client, but it was tailored to WordPress and some other quite specific functionality. I did have a dummy one I used a while ago, just so I could run SCORM locally without it reporting API errors. I'll see if I can dig that out for you as it will be a good boilerplate.
- Miguelvanden090Community Member
Hello SamHill,
Thank you so much for your advise!
I'd really appreciate if you could share the example, so I have a direction to start.
- SamHillSuper Hero
Hi Miguelvanden090 I had a look for anything I could share, and the best I have, which will definitely get you started is a psuedo SCORM 1.2 API. This one was used to run content locally, and so always provided static values for things like student name, id, time etc. This could definitely remain as is, but it could be worth while to modify the code so it pulls the user data from your database, where there is information you could logically put in there. The key SCORM data model you need to work with is the suspend_data, as this stored all of the resume data for Storyline course.
This script would need to be initialised before the Storyline content. The constructor should retrieve any dynamic data from the database first, and then when the Storyline SCORM content runs, this API must be findable by the find API algorithm. Placing the API in the parent, or opener (if appliable) should do the trick, but the API must always be available while the content is running. It will depend on how the courses are presented to the user the method you would use, but a common one is adding the script to and HTML page, which just contains an iframe that loads the SCORM content into it. The API is then easily findable in window.parent.
The data I would recommend that you make dynamic, in the interest of the SCORM course running and resuming as expected is:
#lesson_status (initialised as "not attempted", updated by the content to "incomplete" and then set to "pass/failed/completed" depending on course configuration)
#entry (not set by content, but should be updated to "resume" once run for the first time)
#suspend_data (set by the content - key data for resuming the content)As mentioned, you could also consider #student_name (formatted "Lastname, Firstname") and student ID (just a unique ID such as email address, or system generated ID).
// Pseudo-SCORM 1.2 API Class class API { // Private fields for encapsulation #core_children = "student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,exit,session_time"; #student_id = "defaultID"; #student_name = ""; #lesson_location = ""; // I'm pretty sure Storyline doesn't use this. Most SCORM courses use this to store location, but Storyline uses suspend_data. #credit = "credit"; #lesson_status = "not attempted"; #entry = "ab-initio"; #score_children = "raw"; #score = ""; #exit = ""; #session_time = ""; #total_time = "0000:00:00.00"; #suspend_data = ""; #launch_data = ""; #comments = ""; constructor() { // You need to pull all dynamic data from the database and update the instance variables // #student_id (optional) // #student_name (optional) // #lesson_status (can be left as "not attempted" if not set) // #suspend_data // #entry (can be left as "ab-initio" if suspend_data is empty, or "resume" if suspend_data is not empty) } // cookie = course title to make it unique to this course + cmi + data LMSSetValue(cmi, data) { // console.log('LMSSetValue: '+cmi+' : '+data); switch(cmi) { case "cmi.core.lesson_location": this.#lesson_location = data; break; case "cmi.core.lesson_status": this.#lesson_status = data; break; case "cmi.core.score.raw": this.#score = data; break; case "cmi.core.exit": this.#exit = data; break; case "cmi.core.session_time": this.#session_time = data; break; case "cmi.suspend_data": this.#suspend_data = data; break; case "cmi.comments": this.#comments += data; break; } // **** Now you must update the database with the new values **** // } LMSGetValue(cmi) { let data; switch(cmi) { case "cmi.core._children": data = this.#core_children; break; case "cmi.core.student_id": data = this.#student_id; break; case "cmi.core.student_name": data = this.#student_name; break; case "cmi.core.lesson_location": data = this.#lesson_location; break; case "cmi.core.credit": data = this.#credit; break; case "cmi.core.lesson_status": data = this.#lesson_status; break; case "cmi.core.entry": data = this.#entry; break; case "cmi.core.score._children": data = this.#score_children; break; case "cmi.core.score.raw": data = this.#score; break; case "cmi.core.total_time": data = this.#total_time; break; case "cmi.suspend_data": data = this.#suspend_data; break; case "cmi.launch_data": data = this.#launch_data; break; case "cmi.comments": data = this.#comments; break; } // console.log('LMSGetValue: '+cmi+' : '+data); return data; } LMSInitialize(str) { return true; } LMSCommit(str) { return true; } LMSFinish(str) { return true; } LMSGetLastError() { return 0; } LMSGetErrorString(error) { return; } LMSGetDiagnostic(error) { return; } }
Related Content
- 5 months ago
- 5 months ago