Resuming an articulate story

Nov 15, 2013

When you launch an Articulate story (at least via SCORM Cloud), the question "Would you like to resume where you left off?" pops up.  This must come from the state API, specifically the GET call on activities/state.  Looking at the response from that, you get something like:

25245060ji1001111a010110111100~2C1~2w1cb101001a1a113te1M6Ai00H06Ai00I26Ai0000jDi3000m1118_default6Ai0012pn02Dkfe720118_default0000001000

What format is that in?  How can I use that data to know which slide to go to?  And even if you know, how do you get the browser to go to that slide?  Articulate must be getting (or giving) some information about that.

None of this is documented anywhere.

18 Replies
fbs 419

So what is this data?  Is it some kind of bookmark?  What am I supposed to store via the TinCan state API's /activities/state/?method=PUT, and when I get whatever comes back in /activities/state/?method=GET, what am I supposed to do with it?  How does that get the Articulate story to go to where we left off?

fbs 419

It didn't start out that way, but yes -- it looks like I'm building my own LMS, or at least trying to implement a lot of the Tin Can stuff myself.  I've implemented services for launching stories, and for storing and retrieving Tin Can statements, and all that's working fine.  I figured I would implement the state API, but there is no documentation on what the format of state data is, and especially, how you actually communicate with Articulate to get it to use the resume data.  I guess since I'm using an LMS, I wouldn't be dealing with Flash cookies. So all I have is this weird compressed data with no idea what to do with it.  I wish this were documented somewhere.

Ashley Terwilliger-Pollard

Hi Frank,

As Phil mentioned, suspend data is compressed to allow for more robust storage, the suspend_data string in an LMS debug log isn't human-readable. That is, you won't be able to decipher it. Since you're building your own LMS, I'm not sure this information will help, but figured I'd share here the information on an LMS and Flash cookies:

If you choose Always or Prompt, and if you'll be hosting your content in an LMS, do one of the following:

  • If your LMS supports bookmarking, mark the box labeled When running in LMS, ignore Flash cookie. The LMS will control resume behavior in this scenario.
  • If your LMS does not support bookmarking, uncheck When running in LMS, ignore Flash cookie. The Flash cookie will control resume behavior in this scenario.
fbs 419

So I'm not sure what "if your LMS supports bookmarking" means.  Does that mean that the LMS has implemented the State API, and is dealing with this compressed resume data?   And if it doesn't support bookmarking, I assume that means the state API wasn't implemented, and we would use the Flash cookie so Flash would take care of it.

There seems to be no documentation on what the state API is supposed to return, and how to use this data to influence the Articulate player.

Ashley Terwilliger-Pollard

Hi Frank,

Yes, if the LMS supports bookmarking it is tracking the resume data itself. If it doesn't, the flash cookies will do this. That data is compressed, so I don't have information on what it's returning, but for more information about how it's handled within Storyline I wanted to point you to this kb article. 

fbs 419

Well -- since nobody knows how this data is compressed, and therefore what I should store and retrieve, it seems like it's impossible for me to implement the State API in my LMS, and I will just have to rely on the Flash cookies.  I suppose Rustici probably knows about this, but it seems weird that nobody documents it.  The spec talks about LMS's having to implement the State API, but they then make it impossible to do so.

fbs 419

This is an old thread, but I am revisiting it now since we finally need this capability.  I have this method which is called if I have chosen the resume option in my Articulate story:

[OperationContract, WebInvoke(Method = "*", UriTemplate = "{appId}/activities/state", ResponseFormat = WebMessageFormat.Json)]
public void activitiesstate(string appId, Stream theStream)

With this code:

var method = WebOperationContext.Current.IncomingRequest.Method;
string m = method.ToString();

I can tell whether it's a GET or a PUT.  The framework seems fine -- GET only happens when the course is started, and for every question, I see a PUT, and the parameter "theStream" has the resume data, so I store it.  On the next GET, when I resume the course, I read that data.  It is not working.

My questions come from these statements from a couple of other related posts:

https://community.articulate.com/discussions/articulate-storyline/resume-tin-can-state
https://community.articulate.com/discussions/articulate-storyline/resuming-an-articulate-story#reply-737844

The first one has this statement:

You just need to save the "Content" string which is the bookmark. And then when its requesting that info back, just return that content string by itself and Articulate will resume to that specific spot it saved at.

I'm sure this advice is correct, but I'm not sure how to implement this.  How do I just return that content string by itself?  The method is a void, which I assume the Tin Can framework wants.

The second one has this statement:

Frank, just return that content parameter by itself.  So its a json return that should just say 2h145040981001111000$Xa91010310103xe1C1414e720118_default1414e770158_default00010000  Or whatever your bookmark looks like

Again, how do I just return that content parameter by itself?

Should the code for GET send this data back?  Like I said, the activitiesstate method is a void.  I figured if maybe the GET sent this data back, that when the statements method executes, it would go to the place I want to resume.  But I don't know how to send it back.

I do this in the GET code.  resumeData has the proper string.

WebOperationContext.Current.OutgoingRequest.ContentType = "application/octet-stream";
WebOperationContext.Current.OutgoingResponse.ContentLength = resumeData.Length;

I read that I need to set the ContentType to application/octet-stream.

But this doesn't work yet.  Perhaps I need another line of code to include that data?  I don't quite see how to do that, and since the method is a void, I can't return anything.

Or am I totally off about how this is supposed to work?  I guess my question is:

What do I do in the GET part of this code to get this to work?

Thanks

 

fbs 419

OK -- starting to figure this out and I've proved the concept.  I changed my activities/state method to return a string, and for GET, it now returns that JSON string of the resume data.  I am using application/json as the ContentType and that seems to be working.  Not totally working yet, but I've gotten past the issue that was bugging me.

Thanks to Trip Levine and Nelson Cheng for info in their posts.

Johanna Kisting
fbs 419

OK -- starting to figure this out and I've proved the concept.  I changed my activities/state method to return a string, and for GET, it now returns that JSON string of the resume data.  I am using application/json as the ContentType and that seems to be working.  Not totally working yet, but I've gotten past the issue that was bugging me.

Thanks to Trip Levine and Nelson Cheng for info in their posts.

Any progress on this?