 #  SCORM Shenanigans - PART DEUX 

 

  ![More SCORM Shenanigans](https://cdn.richeyweb.com/images/articles/knowbe4/knowbe4-720p.webp)    - [KnowBe4 Commissioned](https://www.knowbe4.com/)
 


Because of my work described in [Brain Games - SCORM/suspend\_data and xAPI/state](/blog/development/brain-games-scorm-suspend-data-and-xapi-state), I gained the attention of another company looking to divine some wisdom from [data](/blog/development/gpc-dnt-do-not-tracks-toothless-twin "GPC: DNT/Do Not Track’s Toothless Twin") they had collected. It has been a number of years since I did this work, but I just got another email thanking me for my suspend data work and I thought this might be helpful to someone.

[KnowBe4](https://www.knowbe4.com) had commissioned a SCORM module with a quiz, and the module was created in Articulate Storyline. It delivered the quiz as intended, storing [results](/blog/hosting/unavailable-after-white-hat-seo-hack-might-be-paying-off "unavailable_after White-Hat SEO Hack Might Be Paying Off") in its internal suspend [data storage](/blog/development/brain-games-scorm-suspend-data-and-xapi-state "Brain Games - SCORM/suspend_data and xAPI/state") - but the results were not accessible to anyone except Articulate. They commissioned a module to deliver a quiz, but could not access the results. How Articulate manages to keep convincing people to buy their product escapes me.

![](https://cdn.richeyweb.com/images/articles/knowbe4/storyline-salesman.webp)

## Gathering SCORM Test Data

My process began by requesting a series of very specific test results. I needed known results so I could map them to the data structures present within the suspend data. The datasets requested were as follows:

1. All correct answers
2. All incorrect answers
3. 1st half incorrect, 2nd half correct
4. Alternating correct/incorrect starting with correct
5. Alternating correct/incorrect starting with incorrect
 
From these datasets I was able to tease out the data structure, but learned some valuable lessons along the way. Along with the quiz and the requested result datasets, I also requested a 65 question SCORM module so I could learn the base numbering system - but the created quiz stored data completely differently than the 19 question quiz they gave to their employees.

## This Isn't Consistent - at All

The manner in which the data is stored is directly related to the way the quiz is created, the types of each question. I was able to write [software](/blog/development/bug-reports-a-developers-best-friend-not-a-burden "Bug Reports: A Developer's Best Friend, Not a Burden") to decode the data for THIS PARTICULAR quiz, but it wouldn't work on any other quiz data...sorry. The 2 different SCORM quizzes taught me that each will use its own delimiter. I present this code as inspiration, or possibly a starting point to solve YOUR version of this problem. Make no assumptions, what you think might be a delimiter could be data - and what you think could be data might be a delimiter.

One thing did become clear, the quiz data is stored separately of the progress data. Articulate stores it at the end.

After processing, this is an example of the SCORM quiz data extracted from a single test dataset:

 ```
Array<br></br>(<br></br>    [0] => g_default_Visited340034003400340034000000~20232103391Y34003400<br></br>    [1] => g_default_Visited0000021000~20132102olQ340034003400z7w0801k1<br></br>    [2] => g_default_Visited0000212000~26232102Uk~201340034003400q70020141<br></br>    [3] => g_default_Visited000021100~2s132102mc~2d1340034003400z7w0801k1<br></br>    [4] => g_default_Visited0000212000~26232102il~20134003400g600101<br></br>    [5] => g_default_Visited000021200~2j132102sf~2413400340034003400z7w0801k1<br></br>    [6] => g_default_Visited00000211000~27232103fW0~2013400q70020141<br></br>    [7] => g_default_Visited0000212000~28232103uj1~20134003400q70020141<br></br>    [8] => g_default_Visited000021200~2r132102vq~2c1340034003400z7w0801k1<br></br>    [9] => g_default_Visited000021200~2g132103HF0~2013400340034003400z7w0801k1<br></br>    [10] => g_default_Visited0000212000~2W132103Q31S34003400g600101<br></br>    [11] => g_default_Visited000021100~27232103ZY3~20134003400q70020141<br></br>    [12] => g_default_Visited00000210000~27232103Vw1~2013400g600101<br></br>    [13] => g_default_Visited000021100~24132102noU34003400z7w0801k1<br></br>    [14] => g_default_Visited0000211000~27232103WF1~20134003400q70020141<br></br>    [15] => g_default_Visited000021200~28132102DmY3400340034003400z7w0801k1<br></br>    [16] => g_default_Visited00000211000~2c132103eA0$340034003400340034003400340034003400q70020141<br></br>    [17] => g_default_Visited0000~26232102$g~20134003400q70020141<br></br>    [18] => g_default_Visited000021100~2f132102ii~201340034003400z7w0801k1<br></br>    [19] => g_default_Visited00000210000~2b332103XE0~242340034003400g600101<br></br>    [20] => g_default_Visited0000021000~2j132102Ms~241340034003400z7w0801k1<br></br>)
```

## An Amusing Anomaly

### (That had me scratching my head for hours)

Array items 6&amp;7 (questions 7&amp;8) were anomalous in the dataset. My theory is that they were originally in the opposite order, and were changed in Storyline - but the data was stored in the original order. So looking at the -&gt;grade property of the class for this particular dataset, you would see NOT alternating correct/incorrect for items 6&amp;7. Something happened to the module to transpose those two answers, and it was very confusing until I noticed that it occurred identically in both of the alternating datasets.

 ```
Array<br></br>(<br></br>    [0] => 0<br></br>    [1] => 1<br></br>    [2] => 0<br></br>    [3] => 1<br></br>    [4] => 0<br></br>    [5] => 1<br></br><span class="text-decoration-underline"><span class="fw-bold">    [6] => 1</span></span><br></br><span class="text-decoration-underline"><span class="fw-bold">    [7] => 0</span></span><br></br>    [8] => 0<br></br>    [9] => 1<br></br>    [10] => 0<br></br>    [11] => 1<br></br>    [12] => 0<br></br>    [13] => 1<br></br>    [14] => 0<br></br>    [15] => 1<br></br>    [16] => 0<br></br>    [17] => 1<br></br>    [18] => 0<br></br>)
```

## My Apologies

I really wanted to create a class that answered the big question, but sadly I could not. It turns out that the data structures change module-to-module. It would take a long time to figure this problem out, but that's not what I was hired to do. I would need many different examples of every kind of quiz question and combinations of each. Maybe someone would like to pay me to completely ruin Articulate's storage algorithm.

## The Class

KnowBe4 was interested in one thing - getting the scores. They deployed their SCORM quiz, made every employee take it - and then were forced to sit on unusable data until they contacted me to request assistance. In case you're wondering - I love this kind of stuff. This was a great puzzle to solve, and it took me the better part of a week to do it.

I made the properties public for debugging, the only public function is score, which should be self explanatory. If only Articulate would be the good guys and release a [spec](/blog/personal/self-sufficient-solutions-building-trust-with-clients "Self-Sufficient Solutions: Building Trust with Clients") on their encoder/decoder - this wouldn't be necessary. Everyone else stores SCORM data in easily accessible formats.

&lt;?php

/\* KnowB4 iterativeParser Class  
 \* author: Michael Richey  
 \* license: GPLv3  
\*/

class iterativeParser {  
 public $raw = '';  
 public $lines = array();  
 public $grade = array();  
 private $regex = array(  
 // regex to identify the quiz start marker  
 'q1'=&gt;'/(?&lt;data&gt;g\_default\_Visited3\[340\]+~(.\*?)(3210|1541)(.\*?)(\\^8\_default(.\*?)){0,1}(?=(\\^|r7|q7)))/',   
 'qa'=&gt;'/(?&lt;data&gt;g\_default\_Visited0000(.\*?)(?=(\\^|$)))/',  
 );  
 public function \_\_construct($string) {  
 // save the string for later processing  
 $this-&gt;raw = $string;  
 // first, grab all of the relevant data items  
 $this-&gt;capture();  
 // iterate through them to determine status  
 $this-&gt;crawl();  
 }  
 private function capture() {  
 $matches = array();  
 // grabbing the quiz start marker so we can ignore the slides/questions preceeding the quiz  
 // PREG\_OFFSET\_CAPTURE offers the starting character position of the quiz  
 preg\_match($this-&gt;regex\['q1'\],$this-&gt;raw,$matches,PREG\_OFFSET\_CAPTURE);  
 $this-&gt;lines\[\] = $matches\['data'\]\[0\];  
   
   
 $offset = $matches\['data'\]\[1\];

 // start capturing at $offset to prevent capturing the opening slides and video data  
 preg\_match\_all($this-&gt;regex\['qa'\],$this-&gt;raw,$matches,0,$offset);  
 // appending the found data to the lines property  
 $this-&gt;lines = array\_merge($this-&gt;lines,$matches\['data'\]);  
 }  
 private function crawl() {  
 $first = false;  
 foreach($this-&gt;lines as $key=&gt;$line) {  
 // question 16 has a 2nd page that always displays and records data, so we skip it  
 if($key == 17) continue;  
 switch($key) {  
 case 0:  
 // question 1 records incorrect answers on a entry that does not exist when answered correctly  
 $grade = (int)preg\_match('/8\_default/',$line);  
 if($grade) {  
 $this-&gt;grade\[\] = $grade;  
 $first = true;  
 }  
 break;  
 case 1:  
 if(!$first) {  
 $this-&gt;grade\[\] = $this-&gt;zTest($line);  
 }  
 break;  
 case 11:  
 $this-&gt;grade\[\] = (int)!preg\_match('/(1|2)00~/',$line);  
 break;  
 default:  
 // if the question line contains z7 - it's incorrect, we send the inverse integer of true/false to the grade array  
 $this-&gt;grade\[\] = $this-&gt;zTest($line);  
 break;  
 }  
 }  
 }  
 private function zTest($line) {  
 return (int)!preg\_match('/(z7)/',$line);  
 }  
 public function score() {  
 return array\_sum($this-&gt;grade)/count($this-&gt;grade);  
 }  
}



- [      email ](mailto:?subject=SCORM+Shenanigans+-+PART+DEUX&body=https%3A%2F%2Fwww.richeyweb.com%2Fblog%2Fdevelopment%2Fscorm-shenanigans-part-deux)
- [      facebook ](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.richeyweb.com%2Fblog%2Fdevelopment%2Fscorm-shenanigans-part-deux)
- [      x-twitter ](https://twitter.com/intent/tweet?text=SCORM+Shenanigans+-+PART+DEUX%3A+https%3A%2F%2Fwww.richeyweb.com%2Fblog%2Fdevelopment%2Fscorm-shenanigans-part-deux)
- [      linkedin ](http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.richeyweb.com%2Fblog%2Fdevelopment%2Fscorm-shenanigans-part-deux&title=SCORM+Shenanigans+-+PART+DEUX&summary=Because+of+my+work+described+in+Brain+Games+-+SCOR...)
- [      pinterest ](http://pinterest.com/pin/create/button/?url=https%3A%2F%2Fwww.richeyweb.com%2Fblog%2Fdevelopment%2Fscorm-shenanigans-part-deux&media=https%3A%2F%2Fwww.richeyweb.com%2Fimages%2Farticles%2Fknowbe4%2Fstoryline-salesman.webp&description=SCORM+Shenanigans+-+PART+DEUX)
 


 

   [  Previous article: Did I Just Solve Joomla Bot Spam With HashCash?   Did I Just Solve Joomla Bot Spam With HashCash? ](/blog/development/did-i-just-solve-joomla-bot-spam-with-hashcash) [  Next article: GPC: DNT/Do Not Track’s Toothless Twin  GPC: DNT/Do Not Track’s Toothless Twin  ](/blog/development/gpc-dnt-do-not-tracks-toothless-twin)  

##### We Value Your Privacy

 

We use cookies to enhance your experience and for traffic analysis. By continuing to visit this site you agree to our use of cookies.

[Privacy Policy](/privacy-policy)

 Details 

###### Google Tag Manager Items

- Ad Storage
- Ad User Data
- Ad Personalization
- Analytics Storage
- Functionality Storage
- Personalization Storage
- Security Storage
 
 

 

 

 

 

 Decline Accept
```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://www.richeyweb.com/#organization","name":"RicheyWeb","url":"https://www.richeyweb.com/","logo":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/logo/richeyweb.svg","contentUrl":"https://www.richeyweb.com/images/logo/richeyweb.svg","width":{"@type":"QuantitativeValue","value":38,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":38,"unitCode":"PX"},"@id":"https://www.richeyweb.com/#logo"},"image":{"@id":"https://www.richeyweb.com/#logo"},"sameAs":["https://x.com/ComRicheyweb","https://www.facebook.com/RicheyWebDev/","https://www.youtube.com/channel/UCxnVG8BwOvQRO7hVqNX7T2g","https://community.joomla.org/service-providers-directory/listings/115:richeyweb.html"],"description":"RicheyWeb is a custom software developer specializing in Joomla extensions.","ContactPoint":[{"@type":"ContactPoint","url":"https://www.richeyweb.com/contact-us","telephone":"903-873-8460","contactType":"Owner/Administrator","areaServed":["United States",{"@type":"Country","name":"United States","sameAs":["https://en.wikipedia.org/wiki/United_States","https://www.wikidata.org/wiki/Q30","https://g.co/kg/m/09c7w0"]},"European Union",{"@type":"AdministrativeArea","name":"European Union","sameAs":["https://en.wikipedia.org/wiki/European_Union","https://www.wikidata.org/wiki/Q458","https://g.co/kg/m/0_6t_z8"]},"United Kingdom",{"@type":"Country","name":"United Kingdom","sameAs":["https://en.wikipedia.org/wiki/United_Kingdom","https://www.wikidata.org/wiki/Q145","https://g.co/kg/m/07ssc"]},"Australia",{"@type":"Country","name":"Australia","sameAs":["https://en.wikipedia.org/wiki/Australia","https://www.wikidata.org/wiki/Q408","https://g.co/kg/m/0chghy"]},"Canada",{"@type":"Country","name":"Canada","sameAs":["https://en.wikipedia.org/wiki/Canada","https://www.wikidata.org/wiki/Q16","https://g.co/kg/m/0d060g"]},"Russia",{"@type":"Country","name":"Russia","sameAs":["https://en.wikipedia.org/wiki/Russia","https://www.wikidata.org/wiki/Q159","https://g.co/kg/m/06bnz"]},"China",{"@type":"Country","name":"China","sameAs":["https://en.wikipedia.org/wiki/China","https://www.wikidata.org/wiki/Q148","https://g.co/kg/m/0d05w3"]}],"availableLanguage":"en"},{"@type":"ContactPoint","url":"https://www.richeyweb.com/bugs","telephone":"903-873-8460","contactType":"Technical Support","areaServed":["United States",{"@type":"Country","name":"United States","sameAs":["https://en.wikipedia.org/wiki/United_States","https://www.wikidata.org/wiki/Q30","https://g.co/kg/m/09c7w0"]},"European Union",{"@type":"AdministrativeArea","name":"European Union","sameAs":["https://en.wikipedia.org/wiki/European_Union","https://www.wikidata.org/wiki/Q458","https://g.co/kg/m/0_6t_z8"]},"United Kingdom",{"@type":"Country","name":"United Kingdom","sameAs":["https://en.wikipedia.org/wiki/United_Kingdom","https://www.wikidata.org/wiki/Q145","https://g.co/kg/m/07ssc"]},"Australia",{"@type":"Country","name":"Australia","sameAs":["https://en.wikipedia.org/wiki/Australia","https://www.wikidata.org/wiki/Q408","https://g.co/kg/m/0chghy"]},"Canada",{"@type":"Country","name":"Canada","sameAs":["https://en.wikipedia.org/wiki/Canada","https://www.wikidata.org/wiki/Q16","https://g.co/kg/m/0d060g"]},"Russia",{"@type":"Country","name":"Russia","sameAs":["https://en.wikipedia.org/wiki/Russia","https://www.wikidata.org/wiki/Q159","https://g.co/kg/m/06bnz"]},"China",{"@type":"Country","name":"China","sameAs":["https://en.wikipedia.org/wiki/China","https://www.wikidata.org/wiki/Q148","https://g.co/kg/m/0d05w3"]}],"availableLanguage":"en"}],"knowsAbout":["Computer programming",{"@type":"Thing","name":"Computer programming","sameAs":["https://en.wikipedia.org/wiki/Computer_programming","https://www.wikidata.org/wiki/Q80006","https://g.co/kg/m/01mf_"]},"PHP",{"@type":"Thing","name":"PHP","sameAs":["https://en.wikipedia.org/wiki/PHP","https://www.wikidata.org/wiki/Q59","https://g.co/kg/m/060kv"]},"JavaScript",{"@type":"Thing","name":"JavaScript","sameAs":["https://en.wikipedia.org/wiki/JavaScript","https://www.wikidata.org/wiki/Q2005","https://g.co/kg/m/02p97"]},"arduino","Computer forensics",{"@type":"Thing","name":"Computer forensics","sameAs":["https://en.wikipedia.org/wiki/Computer_forensics","https://www.wikidata.org/wiki/Q878553","https://g.co/kg/m/02wxbd"]},"White hat",{"@type":"Thing","name":"White hat","sameAs":["https://en.wikipedia.org/wiki/White_hat_(computer_security)","https://www.wikidata.org/wiki/Q7995625","https://g.co/kg/m/03ns_5"]},"Search engine optimization",{"@type":"Thing","name":"Search engine optimization","sameAs":["https://en.wikipedia.org/wiki/Search_engine_optimization","https://www.wikidata.org/wiki/Q180711","https://g.co/kg/m/019qb_"]},"Search engine marketing",{"@type":"Thing","name":"Search engine marketing","sameAs":["https://en.wikipedia.org/wiki/Search_engine_marketing","https://www.wikidata.org/wiki/Q846132","https://g.co/kg/m/06mw8r"]},"Digital marketing",{"@type":"Thing","name":"Digital marketing","sameAs":["https://en.wikipedia.org/wiki/Digital_marketing","https://www.wikidata.org/wiki/Q1323528","https://g.co/kg/g/122hcnps"]},"Web hosting service",{"@type":"Thing","name":"Web hosting service","sameAs":["https://en.wikipedia.org/wiki/Web_hosting_service","https://www.wikidata.org/wiki/Q5892272","https://g.co/kg/m/014pz4"]},"Email hosting service",{"@type":"Thing","name":"Email hosting service","sameAs":["https://en.wikipedia.org/wiki/Email_hosting_service","https://www.wikidata.org/wiki/Q5368818","https://g.co/kg/m/09w60m"]},"Internet hosting service",{"@type":"Thing","name":"Internet hosting service","sameAs":["https://en.wikipedia.org/wiki/Internet_hosting_service","https://www.wikidata.org/wiki/Q1210425","https://g.co/kg/m/09w5yw"]},"Virtual hosting",{"@type":"Thing","name":"Virtual hosting","sameAs":["https://en.wikipedia.org/wiki/Virtual_hosting","https://www.wikidata.org/wiki/Q588365","https://g.co/kg/m/024mvh"]},"Web performance",{"@type":"Thing","name":"Web performance","sameAs":["https://en.wikipedia.org/wiki/Web_performance","https://www.wikidata.org/wiki/Q7978612","https://g.co/kg/m/0gfj3f1"]},"Web content management system",{"@type":"Thing","name":"Web content management system","sameAs":["https://en.wikipedia.org/wiki/Web_content_management_system","https://www.wikidata.org/wiki/Q45211","https://g.co/kg/m/0615s2"]},"Content management system",{"@type":"Thing","name":"Content management system","sameAs":["https://en.wikipedia.org/wiki/Content_management_system","https://www.wikidata.org/wiki/Q131093","https://g.co/kg/m/0k23c"]},"General Data Protection Regulation",{"@type":"Thing","name":"General Data Protection Regulation","sameAs":["https://en.wikipedia.org/wiki/General_Data_Protection_Regulation","https://www.wikidata.org/wiki/Q1172506","https://g.co/kg/m/0pk_7xs"]},"SERP",{"@type":"Thing","name":"SERP","sameAs":["https://en.wikipedia.org/wiki/SERP","https://www.wikidata.org/wiki/Q2205811","https://g.co/kg/g/11c5szp7kc"]},"Artificial intelligence",{"@type":"Thing","name":"Artificial intelligence","sameAs":["https://en.wikipedia.org/wiki/Artificial_intelligence","https://www.wikidata.org/wiki/Q11660","https://g.co/kg/m/0mkz"]},"Prompt engineering",{"@type":"Thing","name":"Prompt engineering","sameAs":["https://en.wikipedia.org/wiki/Prompt_engineering","https://www.wikidata.org/wiki/Q108941486","https://g.co/kg/g/11p6kpgt_n"]},"E-learning",{"@type":"Thing","name":"E-learning","sameAs":["https://en.wikipedia.org/wiki/E-learning_(theory)","https://www.wikidata.org/wiki/Q182250","https://g.co/kg/g/122czm1f"]},"Sharable Content Object Reference Model",{"@type":"Thing","name":"Sharable Content Object Reference Model","sameAs":["https://en.wikipedia.org/wiki/Sharable_Content_Object_Reference_Model","https://www.wikidata.org/wiki/Q827811","https://g.co/kg/m/06_40"]},"Experience API",{"@type":"Thing","name":"Experience API","sameAs":["https://en.wikipedia.org/wiki/Experience_API","https://www.wikidata.org/wiki/Q7807728","https://g.co/kg/g/1yw9ktxr8"]},"Joomla",{"@type":"Thing","name":"Joomla","sameAs":["https://en.wikipedia.org/wiki/Joomla","https://www.wikidata.org/wiki/Q13167","https://g.co/kg/m/07qb81"]},"Nginx",{"@type":"Thing","name":"Nginx","sameAs":["https://en.wikipedia.org/wiki/Nginx","https://www.wikidata.org/wiki/Q306144","https://g.co/kg/m/02qft91"]},"MySQL",{"@type":"Thing","name":"MySQL","sameAs":["https://en.wikipedia.org/wiki/MySQL","https://www.wikidata.org/wiki/Q850","https://g.co/kg/m/04y3k"]}],"areaServed":["United States",{"@type":"Country","name":"United States","sameAs":["https://en.wikipedia.org/wiki/United_States","https://www.wikidata.org/wiki/Q30","https://g.co/kg/m/09c7w0"]},"European Union",{"@type":"AdministrativeArea","name":"European Union","sameAs":["https://en.wikipedia.org/wiki/European_Union","https://www.wikidata.org/wiki/Q458","https://g.co/kg/m/0_6t_z8"]},"United Kingdom",{"@type":"Country","name":"United Kingdom","sameAs":["https://en.wikipedia.org/wiki/United_Kingdom","https://www.wikidata.org/wiki/Q145","https://g.co/kg/m/07ssc"]},"Australia",{"@type":"Country","name":"Australia","sameAs":["https://en.wikipedia.org/wiki/Australia","https://www.wikidata.org/wiki/Q408","https://g.co/kg/m/0chghy"]},"Canada",{"@type":"Country","name":"Canada","sameAs":["https://en.wikipedia.org/wiki/Canada","https://www.wikidata.org/wiki/Q16","https://g.co/kg/m/0d060g"]},"Russia",{"@type":"Country","name":"Russia","sameAs":["https://en.wikipedia.org/wiki/Russia","https://www.wikidata.org/wiki/Q159","https://g.co/kg/m/06bnz"]},"China",{"@type":"Country","name":"China","sameAs":["https://en.wikipedia.org/wiki/China","https://www.wikidata.org/wiki/Q148","https://g.co/kg/m/0d05w3"]}],"memberOf":["Mensa International",{"@type":"Organization","name":"Mensa International","sameAs":["https://en.wikipedia.org/wiki/Mensa_International","https://www.wikidata.org/wiki/Q184194","https://g.co/kg/m/0140pf"]},"National Rifle Association",{"@type":"Organization","name":"National Rifle Association","sameAs":["https://en.wikipedia.org/wiki/National_Rifle_Association","https://www.wikidata.org/wiki/Q863259","https://g.co/kg/m/0j6f9"]},"CompTIA",{"@type":"Organization","name":"CompTIA","sameAs":["https://en.wikipedia.org/wiki/CompTIA","https://www.wikidata.org/wiki/Q597534","https://g.co/kg/m/040shq"]},"ISFCE LLC",{"@type":"Organization","name":"ISFCE LLC","sameAs":["https://isfce.com","https://g.co/kg/g/11wxm5r0rg"]}],"hasCredential":[{"@type":"EducationalOccupationalCredential","name":"Joomla 3 Certified Administrator","credentialCategory":"Certification","description":"Administrator Exam is the first available Joomla! certification exam","recognizedBy":{"@type":"Organization","name":"Open Source Matters, Inc.","sameAs":["https://en.wikipedia.org/wiki/Open_Source_Matters,_Inc.","https://g.co/kg/g/11f00wvjhz"]},"url":"https://certification.joomla.org/certified-user-directory/michael-richey","about":["Content management system",{"@type":"Thing","name":"Content management system","sameAs":["https://en.wikipedia.org/wiki/Content_management_system","https://www.wikidata.org/wiki/Q131093","https://g.co/kg/m/0k23c"]},"Web content management system",{"@type":"Thing","name":"Web content management system","sameAs":["https://en.wikipedia.org/wiki/Web_content_management_system","https://www.wikidata.org/wiki/Q45211","https://g.co/kg/m/0615s2"]},"Joomla",{"@type":"Thing","name":"Joomla","sameAs":["https://en.wikipedia.org/wiki/Joomla","https://www.wikidata.org/wiki/Q13167","https://g.co/kg/m/07qb81"]}],"educationalLevel":"expert","image":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/contact/badge.webp","contentUrl":"https://www.richeyweb.com/images/contact/badge.webp","width":{"@type":"QuantitativeValue","value":300,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":86,"unitCode":"PX"},"caption":"Joomla 3 Certified Administrator"}},{"@type":"EducationalOccupationalCredential","name":"Certified Computer Examiner","credentialCategory":"Certification","description":"Internationally recognized computer forensics certifiecation","recognizedBy":{"@type":"Organization","name":"ISFCE LLC","sameAs":["https://en.wikipedia.org/wiki/ISFCE_LLC","https://g.co/kg/g/11wxm5r0rg"]},"url":"https://isfce.com/","about":["Digital forensics",{"@type":"Thing","name":"Digital forensics","sameAs":["https://en.wikipedia.org/wiki/Digital_forensics","https://www.wikidata.org/wiki/Q3246940","https://g.co/kg/m/0cnxzfx"]},"Computer forensics",{"@type":"Thing","name":"Computer forensics","sameAs":["https://en.wikipedia.org/wiki/Computer_forensics","https://www.wikidata.org/wiki/Q878553","https://g.co/kg/m/02wxbd"]},"Mobile device forensics",{"@type":"Thing","name":"Mobile device forensics","sameAs":["https://en.wikipedia.org/wiki/Mobile_device_forensics","https://www.wikidata.org/wiki/Q6887097","https://g.co/kg/m/06zp3tp"]},"Network forensics",{"@type":"Thing","name":"Network forensics","sameAs":["https://en.wikipedia.org/wiki/Network_forensics","https://www.wikidata.org/wiki/Q7001032","https://g.co/kg/m/05pb280"]},"Database forensics",{"@type":"Thing","name":"Database forensics","sameAs":["https://en.wikipedia.org/wiki/Database_forensics","https://www.wikidata.org/wiki/Q5227405","https://g.co/kg/m/0cgqsy"]}],"educationalLevel":"expert","image":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/contact/isfce-cce.webp","contentUrl":"https://www.richeyweb.com/images/contact/isfce-cce.webp","width":{"@type":"QuantitativeValue","value":150,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":150,"unitCode":"PX"},"caption":"Certified Computer Examiner"}}],"hasOfferCatalog":{"@type":"OfferCatalog","name":"Web Services","itemListElement":[{"@type":"Offer","itemOffered":{"@type":"Service","name":"Hosting"}},{"@type":"Offer","itemOffered":{"@type":"Service","name":"Development"}},{"@type":"Offer","itemOffered":{"@type":"Service","name":"Search Engine Optimization"}}]}},{"@type":"WebSite","@id":"https://www.richeyweb.com/#website","url":"https://www.richeyweb.com/","name":"RicheyWeb","publisher":{"@id":"https://www.richeyweb.com/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://www.richeyweb.com/search?q={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string","valueMaxLength":256,"valueMinLength":2,"valuePattern":"^[A-Za-z0-9\\s]+$"}},"creator":{"@id":"https://www.richeyweb.com/#organization"},"copyrightHolder":{"@id":"https://www.richeyweb.com/#organization"}},{"@type":"WebPage","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#webpage","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux","name":"SCORM Shenanigans - PART DEUX","description":"Unlocking SCORM suspend data mysteries: How I decoded Articulate Storyline quiz results for KnowBe4, revealing inconsistent data structures. Code included!","isPartOf":{"@id":"https://www.richeyweb.com/#website"},"about":{"@id":"https://www.richeyweb.com/#organization"},"inLanguage":"en-GB"},{"@type":"Article","image":[{"@type":"ImageObject","url":"https://www.richeyweb.com/images/articles/knowbe4/288p/knowbe4-720p.webp","contentUrl":"https://www.richeyweb.com/images/articles/knowbe4/288p/knowbe4-720p.webp","width":{"@type":"QuantitativeValue","value":512,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":288,"unitCode":"PX"},"caption":"More SCORM Shenanigans","representativeOfPage":true},{"@type":"ImageObject","url":"https://www.richeyweb.com/images/articles/knowbe4/knowbe4-720p.webp","contentUrl":"https://www.richeyweb.com/images/articles/knowbe4/knowbe4-720p.webp","width":{"@type":"QuantitativeValue","value":1280,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":720,"unitCode":"PX"},"caption":"More SCORM Shenanigans"}],"headline":"SCORM Shenanigans - PART DEUX","description":"Unlocking SCORM suspend data mysteries: How I decoded Articulate Storyline quiz results for KnowBe4, revealing inconsistent data structures. Code included!","author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"},"datePublished":"2025-03-05T00:00:00+00:00","dateModified":"2026-03-30T00:00:00+00:00","about":["Articulate Storyline","Sharable Content Object Reference Model",{"@type":"Thing","name":"Sharable Content Object Reference Model","sameAs":["https://en.wikipedia.org/wiki/Sharable_Content_Object_Reference_Model","https://www.wikidata.org/wiki/Q827811","https://g.co/kg/m/06_40"]},"Experience API",{"@type":"Thing","name":"Experience API","sameAs":["https://en.wikipedia.org/wiki/Experience_API","https://www.wikidata.org/wiki/Q7807728","https://g.co/kg/g/1yw9ktxr8"]}],"mentions":["suspend data","state","quiz","KnowBe4, Inc.",{"@type":"Organization","name":"KnowBe4, Inc.","sameAs":["https://en.wikipedia.org/wiki/KnowBe4,_Inc.","https://g.co/kg/g/11f019z_85"]},"Search engine optimization",{"@type":"Thing","name":"Search engine optimization","sameAs":["https://en.wikipedia.org/wiki/Search_engine_optimization","https://www.wikidata.org/wiki/Q180711","https://g.co/kg/m/019qb_"]},"Computer Programmer",{"@type":"Thing","name":"Computer Programmer","sameAs":["https://en.wikipedia.org/wiki/Programmer","https://g.co/kg/m/018j6p"]},"Cipher",{"@type":"Thing","name":"Cipher","sameAs":["https://en.wikipedia.org/wiki/Cipher","https://www.wikidata.org/wiki/Q4681865","https://g.co/kg/m/03bwypz"]},"Computer forensics",{"@type":"Thing","name":"Computer forensics","sameAs":["https://en.wikipedia.org/wiki/Computer_forensics","https://www.wikidata.org/wiki/Q878553","https://g.co/kg/m/02wxbd"]},{"@type":"Article","@id":"https://www.richeyweb.com/blog/development/bug-reports-a-developers-best-friend-not-a-burden#article","url":"https://www.richeyweb.com/blog/development/bug-reports-a-developers-best-friend-not-a-burden","name":"Bug Reports: A Developer's Best Friend, Not a Burden","headline":"Bug Reports: A Developer's Best Friend, Not a Burden","image":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/articles/plg_content_interlinked/articles/russian-comments.webp","contentUrl":"https://www.richeyweb.com/images/articles/plg_content_interlinked/articles/russian-comments.webp","width":{"@type":"QuantitativeValue","value":1200,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":675,"unitCode":"PX"},"caption":"Bug Reports: A Developer's Best Friend, Not a Burden"},"author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"}},{"@type":"Article","@id":"https://www.richeyweb.com/blog/hosting/unavailable-after-white-hat-seo-hack-might-be-paying-off#article","url":"https://www.richeyweb.com/blog/hosting/unavailable-after-white-hat-seo-hack-might-be-paying-off","name":"unavailable_after White-Hat SEO Hack Might Be Paying Off","headline":"unavailable_after White-Hat SEO Hack Might Be Paying Off","image":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/articles/unavailable_after/system-meta-robots-unavailable-after.webp","contentUrl":"https://www.richeyweb.com/images/articles/unavailable_after/system-meta-robots-unavailable-after.webp","width":{"@type":"QuantitativeValue","value":863,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":443,"unitCode":"PX"},"caption":"unavailable_after White-Hat SEO Hack Might Be Paying Off"},"author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"}},{"@type":"Article","@id":"https://www.richeyweb.com/blog/development/gpc-dnt-do-not-tracks-toothless-twin#article","url":"https://www.richeyweb.com/blog/development/gpc-dnt-do-not-tracks-toothless-twin","name":"GPC: DNT/Do Not Track’s Toothless Twin","headline":"GPC: DNT/Do Not Track’s Toothless Twin","image":{"@type":"ImageObject","url":"https://www.richeyweb.com/images/articles/dnt/dnt-headstone-thumbnail.webp","contentUrl":"https://www.richeyweb.com/images/articles/dnt/dnt-headstone-thumbnail.webp","width":{"@type":"QuantitativeValue","value":513,"unitCode":"PX"},"height":{"@type":"QuantitativeValue","value":288,"unitCode":"PX"},"caption":"GPC: DNT/Do Not Track’s Toothless Twin"},"author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"}},{"@type":"Article","@id":"https://www.richeyweb.com/blog/development/brain-games-scorm-suspend-data-and-xapi-state#article","url":"https://www.richeyweb.com/blog/development/brain-games-scorm-suspend-data-and-xapi-state","name":"Brain Games - SCORM/suspend_data and xAPI/state","headline":"Brain Games - SCORM/suspend_data and xAPI/state","author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"}},{"@type":"Article","@id":"https://www.richeyweb.com/blog/personal/self-sufficient-solutions-building-trust-with-clients#article","url":"https://www.richeyweb.com/blog/personal/self-sufficient-solutions-building-trust-with-clients","name":"Self-Sufficient Solutions: Building Trust with Clients","headline":"Self-Sufficient Solutions: Building Trust with Clients","author":{"@type":"Person","name":"Michael Richey","url":"https://www.richeyweb.com/contact-us","@id":"https://www.richeyweb.com/contact-us#person"}}],"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#article","isPartOf":{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#webpage"},"publisher":{"@id":"https://www.richeyweb.com/#organization"},"citation":[{"@type":"CreativeWork","@id":"https://www.richeyweb.com/blog/development/brain-games-scorm-suspend-data-and-xapi-state#article","url":"https://www.richeyweb.com/blog/development/brain-games-scorm-suspend-data-and-xapi-state","name":"Brain Games - SCORM/suspend_data and xAPI/state"},{"@type":"CreativeWork","@id":"https://www.knowbe4.com#creativework","url":"https://www.knowbe4.com"}],"keywords":"SCORM, suspend data, xAPI, data, quiz, results, Articulate Storyline, data storage, data structure, test results, correct answers, incorrect answers, alternating correct/incorrect, quiz data, progress data, delimiter, software, data extraction, array items, grade property, data structures, quiz question, combinations, scores, public function, encoder/decoder, spec, accessible formats, KnowBe4, module, SCORM module","articleSection":"Development","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux","hasPart":[{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-gathering-scorm-test-data_2_1"},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-this-isnt-consistent-at-all_2_2"},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-an-amusing-anomaly_2_3"},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-that-had-me-scratching-my-head-for-hours_3_4"},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-my-apologies_2_5"},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-the-class_2_6"}]},{"@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex","@type":"ItemList","name":"SCORM Shenanigans - PART DEUX","numberOfItems":6,"itemListElement":[{"@type":"ListItem","position":1,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-gathering-scorm-test-data_2_1","name":"Gathering SCORM Test Data","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-gathering-scorm-test-data_2_1"}},{"@type":"ListItem","position":2,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-this-isnt-consistent-at-all_2_2","name":"This Isn't Consistent - at All","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-this-isnt-consistent-at-all_2_2"}},{"@type":"ListItem","position":3,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-an-amusing-anomaly_2_3","name":"An Amusing Anomaly","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-an-amusing-anomaly_2_3"}},{"@type":"ListItem","position":4,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-that-had-me-scratching-my-head-for-hours_3_4","name":"(That had me scratching my head for hours)","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-that-had-me-scratching-my-head-for-hours_3_4"}},{"@type":"ListItem","position":5,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-my-apologies_2_5","name":"My Apologies","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-my-apologies_2_5"}},{"@type":"ListItem","position":6,"item":{"@type":"WPHeader","@id":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#articleindex-toc-the-class_2_6","name":"The Class","url":"https://www.richeyweb.com/blog/development/scorm-shenanigans-part-deux#toc-the-class_2_6"}}]}]}
```
