Storyline 360 suspend data handling - javascript

Good Day!
Being new to SCORM, I was wondering how Storyline 360 handles its suspend data. Does it affect the how it handles suspend data based on the LMS that you are using?
I have a scene that will generate a certificate for the learners upon completion. It defaults to the name that the LMS has and will auto-fill the input when the timeline starts.
One of the requirements for this is when a user changes the input to whichever name they desire, it should be saved and be retrieved upon their next visit. I used SCORM Cloud as a test LMS. My observation is that, for each relaunch of the course, it doesn't retrieve the name that I inputted from the last session through SetVar() and GetVar() methods.
I found a work-around by using lmsAPI.SetDataChunk to set the name then used lmsAPI.GetDataChunk to retrieve it. It worked well and fine but when it was deployed in our LMS it still retrieves something but, it is gibberish.
This is what I currently have by getting DataChunk:
This code will check if there were any saved names from previous sessions through GetDataChunk()
var player = GetPlayer();
var student = lmsAPI.GetStudentName();
var suspenddata = lmsAPI.GetDataChunk();
var username= player.GetVar("userName");
var displayname = "!";
if(suspenddata){
console.log("suspenddata is:"+suspenddata);
username=suspenddata;
}
if(!username){
console.log("username is null, setting to: "+student);
username=student;
}
player.SetVar("userName",username);
if(username){
displayname=" "+username+"!";
}
player.SetVar("displayName",displayname);
player.SetVar("nameInput",username);
When the user decides to change their name it will be saved to the storyline variable via SetVar() and is also passed to SetDataChunk() as shown below
var player = GetPlayer();
var nameinput = player.GetVar("nameInput");
var username = player.GetVar("userName");
var displayname = "!";
if(nameinput){
console.log("nameinput is:"+nameinput);
username = nameinput;
displayname=" "+username+"!";
player.SetVar("displayName",displayname);
player.SetVar("userName",nameinput);
lmsAPI.SetDataChunk(username);
}else{
console.log("nameinput is null");
}
This code works well when I deployed it to SCORM Cloud but as I said before, it would retrieve gibberish when deployed to our own LMS. I haven't tried deploying a version of this where it won't use DataChunk but even then the Storyline variables should be saved to the suspend data when deployed to SCORM Cloud but it doesn't. I am at my wit's end so any help would be appreciated. Thanks!

Related

Webhooks in Bitcoin Transactions - How to Apply Business Logics?

I want to start accepting Bitcoin on my website.
In order to do that, I wrote the following piece of code, but I truly struggle to understand how I can implement proper business logic after that the transaction is completed.
Here is the code:
<html>
<head>
<title>Pay with Bitcoin</title>
<script>
//Gets the URL of the Webpage and gets the price value of this transaction in USD.
//For simplicity Here the Value is passed in the URL.
//However in production you wanna use POST instead of GET.
const myUrl = window.location.href;
const url = new URL(myUrl);
const usdPrice = url.searchParams.get("price");
//This is the function where all the magin happens
const showQR = () => {
//URL of the api which will provide us with current BTC exchange rate
const apiUrl = "https://blockchain.info/ticker";
const hr = new XMLHttpRequest();
hr.open('GET', apiUrl, true);
hr.onreadystatechange = function(){
//Make sure the API sent a valid response
if(hr.readyState == 4){
let ticker = JSON.parse(hr.responseText);
//Get last BTC/USD exchange value from the API , then convert Price from USD to BTC
let BTCprice = ticker.USD.last;
let btcToPay = usdPrice / BTCprice;
//Make sure you have just 8 decimal points in your BTC price!!
btcToPay = btcToPay.toFixed(8);
//Use google API (or other...) to create the QR code. Pass on your btc public address and
//the amount (btc price) dynamically created. Message and label parameters can be dynamic too.
let qrurl = "https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl=bitcoin:1BAnkZn1qW42uRTyG2sCRN9F5kgtfb5Bci?amount="+btcToPay+"%26label=CarRental%26message=BookingID123456";
//Populate the 'btc' DIV with QR code and other info...
document.getElementById('btc').innerHTML = "<img src=" +qrurl+"><br> <span class = 'greenMoney'>" + usdPrice + " usd / " + btcToPay + " BTC </span>";
}
}
hr.send();
};
</script>
</head>
<body onload = "showQR()">
<h1>Pay with BitCoin</h1>
<div id = "btc">
</div>
</body>
</html>
This code does the following:
Gets current USD/BTC exchange rate using the blockchain API.
takes the price in USD for the URL and converts it into BTC
generates a QR code using google API.
Embeds the price, label and message into the QR code
Renders the QR code in a DIV
I ve also set up a web hook service which will be listening to new transactions happening in the specified wallet address. Then a callback to my server is made, by mean of a POST request.
The problem is: the label and message parameters passed to the QR code will not be written in the blockchain.
They are just a handy reference for the customer to remind him what that specific transaction paid for.
As a result the callback to my server is practically useless.
In fact, the callback doesn't return any Booking Id or any other piece of information which could help me to understand who paid for what. Needless to say, in this scenario no business logic is possible: I can't update the order status on my DB, I can't send a confirmation email to the right customer.
How can I embed relevant information (e.g. Booking ID) into the BTC payment, ideally through the QR code?
If this is possible, how can I retrieve this information later on when my server receives the callback informing me that a new payment was made to my BTC wallet?
In short, you can't.
When accepting payments, you are supposed to give each invoice a new BTC address. This way, when you receive notification of an incoming transaction, you can check the receiving address to see which invoice is being paid, and compare the received amount against the expected amount.
Note
Technically, you could embed stuff like a order ID into an OP_RETURN. However, most wallets don't support transactions like that, and any users who want to pay you from an exchange account would be unable to comply.
#Raghav Sood thank you for your input which routed me to the right direction.
Using NodeJS/Express/MongoDB in the backend, I managed to implement a solution which I would like to share here.
Before starting, I wanna make a big disclaimer: this solution is not the only one, it is not the best one, it is not the fastest and probably it is not the most elegant.
Anyway, this solution has the advantage of not relying on packaged third parties solutions. This is in line with the spirit of the whole "no intermediation" philosophy of the bitcoin community. Most imortantly, your XPub always stay in your server and is NOT shared with any external service, which is probably the wisest approach.
Having said that, here is how one can show dynamic unique BTC addresses to customers:
First of all , I put in place a counter which keeps track of how many btc addresses were created for customers from a my HD wallet.
This is important to make sure than you never present the same address twice to customers, which is good for privacy of all parties and also for the sake of implementing business logic in your app.
In order to do this, I store a "counter value" into my DB. Everytime someone visits the BTC payment page, this value is retrived from mongo using a "dealCount" function and is assigned to a "serialPay" variable, which is equal to the value gotten from Mongo + 1. In the backend, the code would be something like this:
`function dealCount(){`
return new Promise(function(resolve, reject){
Deal.find({_id: "ID_OF_OBJ_WHERE_YOU_STORE_COUNTER"}, function(err, data){
if(err){
console.log(err);
}
resolve(data[0].serialDeal + 1);
})
})
};
The new value obtained (which later on will be saved again into Mongo in order to keep track of addresses created) is used to generate the new BTC public address for the customer at hand. If you keep reading you will see how.
To create new public addresses dynamically, one needs the xPub Key of his or her HD Wallet. If one is coding in NodeJS there are a couple of libraries (which can be imported into the server) that will enable this operation rather easily: bitcoinjs-lib and/or bitcore-lib. Personally I opted for Bitcore-lib, because there are less dependencies to deal with and I found the supporting material easier to digest.
Codewise, address generation goes as follows:
const bitcore = require('bitcore-lib');
app.post("/pay.html", urlencodedParser, function(req, res){
let serialPay = dealCount();
serialPay.then(function(serialPay){
const pub = new bitcore.HDPublicKey('INSERT_HERE_YOUR_XPUB_KEY');
let derivedHdPk = pub.derive('m/0/'+serialPay);
let derivedPk = derivedHdPk.publicKey;
let myDynAddress = new bitcore.Address(derivedPk);
res.render('pay', {myDynAddress: myDynAddress});
});
});
Then, using EJS as a templating engine, I could easily make the receiving bitcoin address dynamic in the front-end (/pay.ejs):
let myDynAddress = "<%=myDynAddress%>";
let qrurl = "https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl=bitcoin:"+myDynAddress+"?amount="+btcToPay+"%26label=CarRental";
This will generate the QR Code Dynamically. In the original question, one can see how to render that into the webpage. In the meantime one should also put in place a function to store the updated "serialPay" counter back to the DB.
At this point one should only start monitoring incoming (non-confirmed) payments to the dynamic BTC address generated. A simple way to do it, is using the blockchain.info websocket API. When the payment arrives, things go forward as suggested by #Raghav Sood: one checks the incoming transaction making sure the customer paid the right amount to the right address.
Now you know who paid for what and all sorts of business logics can be triggered.

about local storage.getItem()

I'm a new learner for API, and I have a quesion about local storage. This is a code example from my javascript book:
if (Modernizr.localstorage) {
var txtUsername = document.getElementById('username');
var txtAnswer = document.getElementById('answer');
txtUsername.value = localStorage.getItem('username');
txtAnswer.value = localStorage.getItem('answer');
txtUsername.addEventListener('input', function () {
localStorage.setItem('username', txtUsername.value);
}, false);
txtAnswer.addEventListener('input', function () {
localStorage.setItem('answer', txtAnswer.value); }, false);
}
}
I want to ask why should we "localStorage.getItem()" part? Cause I think if user type their username, then we can get their names just from the variable "txtUsername" cause I thought it should be setItem first and then getItem. Thank you!
Local storage is used to store small amounts of data on the client side. What does your code ?!
For example: A user visited the site for the first time and complete the inputs, , the data stored in the local store. The user closed the browser. The next day he again went to the site to fill out the form again, and its data is already filled. Conveniently!
Also we can use local storage as js object
txtUsername.value = localStorage.getItem('username');
txtUsername.value = localStorage.username;
txtUsername.value = localStorage['username'];
The thing is, it works just as you said.
It's just, when person types data in the textbox he uses setItem - that what 'input' eventListener used for
Think of LocalStorage as of really light database that keeps data even when user closes the page
But since it can store data when page is closed, you want to show the content of it in the textbox - and that's why author uses 'getItem' on start

Restrict number of users in a session in vline

Can I restrict the number of users in a session? Is there any option in vline.session? Please guide if this can be done by writing custom javascript.
EDIT:
Referring to https://vline.com/developer/docs/vline.js/vline.MediaSession#examples, a two party call controller is explained. I want to ask is there any way to restrict number of users in a session? There is no such option present in session's docs. Is it supported as a part of the API?
If this can be done using custom javascript, how?
As a part of my effort, I have tried to implement vline-django examples, but could not find a section in documentation that addresses this issue.
EDIT 2: The code that is working for me.
var vlineClient = (function(){
var client, session,
authToken = {{ user|vline_auth_token|safe }},
serviceId = {% vline_service_id %},
profile = {{ user|vline_user_profile|safe }};
// Create vLine client
window.vlineClient = client = vline.Client.create({"serviceId": serviceId, "ui": true});
// Add login event handler
client.on('login', onLogin);
// Do login
client.login(serviceId, profile, authToken);
function onLogin(event) {
session = event.target;
// Find and init call buttons
var callButtons = document.getElementsByClassName('callbutton');
for (var i=0; i < callButtons.length; ++i) {
initCallButton(callButtons[i]);
}
}
// add event handlers for call button
function initCallButton(button) {
var userId = button.getAttribute('data-userid');
// fetch person object associated with username
session.getPerson(userId).done(function(person) {
// update button state with presence
function onPresenceChange() {
button.setAttribute('data-presence', person.getPresenceState());
}
// set current presence
onPresenceChange();
// handle presence changes
person.on('change:presenceState', onPresenceChange);
// start a call when button is clicked
button.addEventListener('click', function() {
person.startMedia();
});
});
}
return client;
})();
How do I move ahead?
Reference: https://vline.com/developer/docs/vline.js/
if i understand correctly the OP is trying to make a multi-user chat room - this is also what i wanted to do with vline and because i wanted a/v chat as well the number of participants should obviously be capped - it appears that the term 'session' is causing the confusion here so i will refrain from using it
i worked around this by creating a fixed number of users in a db and handling authentication
myself before actually associating a visitor with one of the prepared users - so some javascript logs in each visitor as one of those existing 'anonymous' users and sets only a logged_in? flag in the db so that the next visitor will log in as the next vacant user slot and when all slots are occupied the visitor gets a "chat room full - try again later" response
probably not the most elegant solution - for example the visitor chosen usernames are stored client-side and must be re-assigned to one of the user-definable vline session vars so it can be passed along with each message and the logged_in? db flag needs to be reset when the user exits
note that this was almost a year ago so im a bit foggy on exactly what i did but my app (rails) in up on github if youre interested to fork it - also i should add that although this sort of thing wasnt strictly supported by the vline API at the time there were at least some hints that some analogous feature was being prepared for so there may be some API support for this now - i did notice since then that they have released a "chat room demo" app on github and i would expect that their implementation is more concise than mine so you may want to look at that first - my app tho does have a mostly complete UI with gravatars and collaboration is welcomed

Lotus notes automation from browser

I have been trying to automate Lotus Notes mail fillup from a browser interface.
After refering to Richard Schwartz's answer, i came up with this piece of code using the Lotus.NotesSession class.
function SendScriptMail() {
var mToMail = document.getElementById('txtMailId').value
var mSub = document.getElementById('txtSubject').value
var mMsg = document.getElementById('txtContent').value
var Password = "yyy"
alert("1");
var MailFileServer = "xxx.com"
var MailFile = "C:\Program Files\IBM\Lotus\Notes\mail\user.nsf"
alert("2")
var Session;
var Maildb;
var UI;
var NewMail;
var From = "user#xxx.com"
try {
alert("3")
// Create the Activex object for NotesSession
Session = new ActiveXObject("Lotus.NotesSession");
alert("4")
if (Session == null) {
throw ("NoSession");
} else {
Session.Initialize(Password);
// Get mail database
Maildb = Session.GetDatabase(MailFileServer, MailFile);
alert("5")
if (Maildb == null) {
throw ("NoMaildb");
} else {
NewMail = MailDB.CreateDocument();
if (MailDoc == null) {
throw ('NoMailDoc');
} else {
// Populate the fields
NewMail.AppendItemValue("Form", "Memo")
NewMail.AppendItemValue("SendTo", mToMail)
NewMail.AppendItemValue("From", From)
NewMail.AppendItemValue("Subject", mSub)
NewMail.AppendItemValue("Body", mMsg)
NewMail.Save(True, False)
NewMail.Send(False)
}
}
}
} catch (err) {
// feel free to improve error handling...
alert('Error while sending mail');
}
}
But now, alerts 1,2,3 are being trigerrd, and then the counter moves to the catch block. The lotus notes session is not being started.
In a powershell script that I was previously looking at there was a code regsvr32 "$NotesInstallDir\nlsxbe.dll" /s that was used before the Session = new ActiveXObject("Lotus.NotesSession");. Is there something similar in javascript too, if so how do i invoke that dll.
I think I've realised where I am going wrong. According to me, upto alert("5") things are good. But since Lotus.NotesSession doesn't have a CreateDocument() method, it is throwing the error. I am not sure how to create the document and populate the values though.
Since you've chosen to use the Notes.NotesUIWorkspace class, you are working with the Notes client front-end. It's running, and your users see what's happening on the screen. Are you aware that there's a set of back-end classes (rooted in Lotus.NotesSession) instead of Notes.NotesSession and Notes.NotesUIWorkspace) that work directly with Notes database data, without causing the Notes client to grab focus and display everything that you're doing?
Working with the front-end means that in some cases (depending on the version of Notes that you are working with) you're not going to be working directly with the field names that are standard in Notes messages as stored and as seen in the back-end. You're going to be working with names used as temporary inputs in the form that is used to view and edit the message. You can see these names by using Domino Designer to view the Memo form.
Instead of using 'SendTo', try using:
MailDoc.Fieldsettext('EnterSendTo', mToMail)
Regarding the Body field, there's no temporary field involved, however you haven't really explained the difficulty you are having. Do you not know how to display the interface that you want in the browser? Do you not know how to combine different inputs into a single FieldSetText call? Or are you just dissatisfied with the fact that FieldSetText can't do any fancy formatting? In the latter case, to get more formatting capability you may want to switch to using the back-end classes, which give you access to the NotesRichTextItem class, which has more formatting capabilities.

How do I create an Outlook Task to a specific mailbox using Javascript?

I am attempting to automate the creation of an Outlook Task using JavaScript in a local IE-only environment (using Outlook 2007/2010). I have been able to do so using an Outlook ActiveX object, with one minor catch, my method creates the task in the user's default mailbox. (The 9 in the code below is the enumerated constant for the task folder)
What I need to do is create the task in a separate shared mailbox. I am looking for a way to set the task to the specific shared mailbox (that the user has access to) by name rather than the user's personal (default) folder. What I have so far is below.
function createTask() {
//create Outlook object and map to the task folder
var outlookApp = new ActiveXObject("Outlook.Application");
var nameSpace = outlookApp.getNameSpace("MAPI");
var mailFolder = nameSpace.getDefaultFolder(9); //olFolderTask
//create the task
var task = mailFolder.Items.add('IPM.Task');
//set task properties
task.Subject = 'Subject';
task.Body = "Body of Task";
task.StartDate = "01/01/2012";
task.DueDate = "06/01/2012";
task.ReminderSet = true;
task.Save();
}
I have found some potentially useful information here, with an alternate to the getDefaultFolder function in GetFolderFromID, but I have not been able to find a way using JavaScript/ActiveX to get the needed EntryID just from the Shared Mailbox's name.
I am new to working with Outlook, any suggestions or guidance would be appreciated. Thanks!
Try using the Session.Stores as discussed in this SO post. It contains all the available mailbox stores (Store) a user has in their profile.
Once you've identified the Store you're after, you can use Store.GetDefaultFolder to retrieve the shared mailbox task Folder you are interested in.

Categories