Messenger Plus Script update checking tutorial
Category: code |
Posted By: dt |
Posted On: 11/11/2007
This is a tutorial on Script version checking and updating
tutorial by -dt- (Matt Labrum)
So you've made a script, and you want to make sure your users have the most current version of your script.
Well you've come to the right place. This tutorial is about exactly that!
Now I'm going to assume a few things
1) you're not a complete noob
2) you're not segosa
3) you have a script (If you don't just create a new one)
4) the script must have a ScriptInfo.xml file
5) somewhere online where you can upload files
Now that thats out of the way, we can get to the fun part
A few reference documents for later:
XMLHttpRequest - http://msdn2.microsoft.com/en-us/library/ms535874.aspx
XMLDomDocument - http://msdn2.microsoft.com/en-us/library/ms757878.aspx
Now open up your ScriptInfo.xml file it should look something like the one shown below
Code:
You should notice the Version tag, this is one of the core tags needed in our update checker. You should also notice that there aren't any tags to specify an update location.
This is a problem since our update checker needs to find the new version information somewhere, so lets add a <Update> section in our ScriptInfo.xml and add some updater tags
Code:
Ok so now we have alot more information for our script to use, but I bet you're looking at me like "dt what the hell are all those tags" well
I'll explain each one
ScriptInfoUrl - the location of the updated scriptInfo.xml (our checker will download this file and compare it to the current one)
PlscLocation - Plsc to download if our update checker detects that we need updating
ReleaseNotes - optional, the changelog to display to the user
DateReleased - optional, the date the new version was released
Now i bet you're wondering how the update checker will work. Well, inside your script's OnEvent_Initialize you'll trigger UpdateChecker.Check(), which will then download the update xml (gets this value from Update/ScriptInfoUrl).
Once the updateXML is downloaded it will trigger UpdateChecker.parseUpdate() which then compares the local ScriptInfo.xml <version> tag with the downloaded ScriptInfo.xml <version> tag.
If it's a newer version then the script will display a dialog to the user asking them if they want to update, if they say yes then your script will download the plsc file and run it.
Diagram of how all this fits together

(click for a bigger image)
Now to make the javascript for the above process
Create a new file called UpdateChecker.js
Inside that file we'll create an Object called UpdateChecker, with the functions we need
Code:
Now we need to start filling the functions and making it do something
We will start with the checking code
Code:
Now this can't do anything on its own because it requires the object from ParseScriptInfo, so we will implement that and then test the script
Code:
The full code so far
Code:
Now if you want to test the above code, add the following to your OnEvent_Initialize
Code:
Make sure you have a copy of your ScriptInfo.xml uploaded at the location specified at ScriptInfoUrl, otherwise the code will fail.
Now run the script, if your uploaded ScriptInfo.xml file has a greater version than the local ScriptInfo.xml it should display "Update is newer"
Ok, so now we need to do something if the update is newer, we will call the UpdateChecker.AskUser() method to ask the user if they want to update.
We will just display a messagebox with yes or no buttons, in your own script I recommend that you create a fancy GUI
Code:
And finally we need to create the GetNewPlsc function to open the Plsc file.
Now in your own scripts you should use MsgPlus.DownloadFile to download the plsc and then use
Code:
to run it
but in this example we will simply execute the url
Code:
Ok so lets look at the code so far.
It should download the new ScriptInfo.xml and compare the versions, if theres an update then it will open the PlscLocation
Code:
tutorial by -dt- (Matt Labrum)
So you've made a script, and you want to make sure your users have the most current version of your script.
Well you've come to the right place. This tutorial is about exactly that!
Now I'm going to assume a few things
1) you're not a complete noob
2) you're not segosa
3) you have a script (If you don't just create a new one)
4) the script must have a ScriptInfo.xml file
5) somewhere online where you can upload files
Now that thats out of the way, we can get to the fun part
A few reference documents for later:
XMLHttpRequest - http://msdn2.microsoft.com/en-us/library/ms535874.aspx
XMLDomDocument - http://msdn2.microsoft.com/en-us/library/ms757878.aspx
Now open up your ScriptInfo.xml file it should look something like the one shown below
Code:
<ScriptInfo xmlns="urn:msgplus:scripts" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:msgplus:scripts PlusScripts.xsd"> <Information> <Name>updateExample</Name> <Description>This script is an example script to test update checking</Description> <AboutUrl>http://blog.thedt.net</AboutUrl> <Version>1.00</Version> </Information> </ScriptInfo>
You should notice the Version tag, this is one of the core tags needed in our update checker. You should also notice that there aren't any tags to specify an update location.
This is a problem since our update checker needs to find the new version information somewhere, so lets add a <Update> section in our ScriptInfo.xml and add some updater tags
Code:
<ScriptInfo xmlns="urn:msgplus:scripts" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:msgplus:scripts PlusScripts.xsd"> <Information> <Name>updateExample</Name> <Description>This script is an example script to test update checking</Description> <AboutUrl>http://blog.thedt.net</AboutUrl> <Version>1.00</Version> <Update> <ScriptInfoUrl>http://yoursite.com/updateExample/ScriptInfo.xml</ScriptInfoUrl> <PlscLocation>http://yoursite.com/updateExample/updateExample.plsc</PlscLocation> <ReleaseNotes> - First release </ReleaseNotes> <DateReleased>10/11/2007</DateReleased> </Update> </Information> </ScriptInfo>
Ok so now we have alot more information for our script to use, but I bet you're looking at me like "dt what the hell are all those tags" well
ScriptInfoUrl - the location of the updated scriptInfo.xml (our checker will download this file and compare it to the current one)
PlscLocation - Plsc to download if our update checker detects that we need updating
ReleaseNotes - optional, the changelog to display to the user
DateReleased - optional, the date the new version was released
Now i bet you're wondering how the update checker will work. Well, inside your script's OnEvent_Initialize you'll trigger UpdateChecker.Check(), which will then download the update xml (gets this value from Update/ScriptInfoUrl).
Once the updateXML is downloaded it will trigger UpdateChecker.parseUpdate() which then compares the local ScriptInfo.xml <version> tag with the downloaded ScriptInfo.xml <version> tag.
If it's a newer version then the script will display a dialog to the user asking them if they want to update, if they say yes then your script will download the plsc file and run it.
Diagram of how all this fits together

(click for a bigger image)
Now to make the javascript for the above process
Create a new file called UpdateChecker.js
Inside that file we'll create an Object called UpdateChecker, with the functions we need
Code:
var UpdateChecker = function(){ this.Check(); }; UpdateChecker.prototype = { "Check" : function(){}, //This function will parse the local XML file and trigger the download the update ScriptInfo.xml "ParseScriptInfo" : function(xml){}, //returns an object with the parsed ScriptInfo information "AskUser" : function(){}, "GetNewPlsc" : function(){} }
Now we need to start filling the functions and making it do something
We will start with the checking code
Code:
"Check" : function(){ //This function will parse the local XML file and trigger the download the update ScriptInfo.xml var scriptInfo = this.ParseScriptInfo(); //will return an object in the form of {"Version" : 1.0, "UpdateUrl" : "http://.../scriptInfo.xml", "ReleaseNotes" : "", "DateReleased" : ""} this.details = scriptInfo; //add the details to the object, incase the script wants to use this for something Interop.Call("Wininet.dll", "DeleteUrlCacheEntryW", scriptInfo.UpdateUrl); //make sure theres no cached version of the update var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // Create a new XMLHttp request object to download our update ScriptInfo.xml // see http://msdn2.microsoft.com/en-us/library/ms536648.aspx for details on the open method xmlhttp.open("GET", scriptInfo.UpdateUrl, true) //open a new xmlhttp request to the updateUrl and set it to asynchronous operation var thisd = this; //create a reference to the current scope to use in the callback //see http://msdn2.microsoft.com/en-us/library/ms534308.aspx for more information on the onreadystatechange callback xmlhttp.onreadystatechange = function(){ if(xmlhttp.readyState == 4){ var updateInfo = thisd.ParseScriptInfo(xmlhttp.responseXML); //parses the update scriptInfo and returns an object if(updateInfo.Version > scriptInfo.Version){ //if version is newer Debug.Trace("Update is newer"); }else{ Debug.Trace("Update Is older"); } } } xmlhttp.send(); }
Now this can't do anything on its own because it requires the object from ParseScriptInfo, so we will implement that and then test the script
Code:
"ParseScriptInfo" : function(xml){ //returns an object with the parsed ScriptInfo information if(typeof(xml) == "undefined"){ //if no xml is passed, we will load the local script info var xml = new ActiveXObject("Microsoft.XMLDOM"); xml.async = false; xml.load(MsgPlus.ScriptFilesPath + "\\" + "ScriptInfo.xml"); } //object to return var ob = { "Version" : 0, "UpdateUrl" : "", "PlscLocation" : "", "ReleaseNotes" : "", "DateReleased" : "" }; //get the Update node values ob.UpdateUrl = xml.selectSingleNode("//Update/ScriptInfoUrl").text; ob.PlscLocation = xml.selectSingleNode("//Update/PlscLocation").text; ob.ReleaseNotes = xml.selectSingleNode("//Update/ReleaseNotes").text; ob.DateReleased = xml.selectSingleNode("//Update/DateReleased").text; var version = xml.selectSingleNode("//Version").text; //convert the version to a real number, parseInt chops off the decimal points so we cheat and * it by 1 ob.Version = version * 1; return ob; //return our object }
The full code so far
Code:
var UpdateChecker = function(){ this.Check(); }; UpdateChecker.prototype = { "Check" : function(){ //This function will parse the local XML file and trigger the download the update ScriptInfo.xml var scriptInfo = this.ParseScriptInfo(); //will return an object in the form of {"Version" : 1.0, "UpdateUrl" : "http://.../scriptInfo.xml", "ReleaseNotes" : "", "DateReleased" : ""} this.details = scriptInfo; //add the details to the object, incase the script wants to use this for something Interop.Call("Wininet.dll", "DeleteUrlCacheEntryW", scriptInfo.UpdateUrl); //make sure theres no cached version of the update var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // Create a new XMLHttp request object to download our update ScriptInfo.xml // see http://msdn2.microsoft.com/en-us/library/ms536648.aspx for details on the open method xmlhttp.open("GET", scriptInfo.UpdateUrl, true) //open a new xmlhttp request to the updateUrl and set it to asynchronous operation var thisd = this; //create a reference to the current scope to use in the callback //see http://msdn2.microsoft.com/en-us/library/ms534308.aspx for more information on the onreadystatechange callback xmlhttp.onreadystatechange = function(){ //because this refers the function we have to use thisd when refering to our class if(xmlhttp.readyState == 4){ var updateInfo = thisd.ParseScriptInfo(xmlhttp.responseXML); //parses the update scriptInfo and returns an object if(updateInfo.Version > scriptInfo.Version){ //if version is newer Debug.Trace("Update is newer"); }else{ Debug.Trace("Update Is older"); } } } xmlhttp.send(); }, "ParseScriptInfo" : function(xml){ //returns an object with the parsed ScriptInfo information if(typeof(xml) == "undefined"){ //if no xml is passed, we will load the local script info var xml = new ActiveXObject("Microsoft.XMLDOM"); xml.async = false; xml.load(MsgPlus.ScriptFilesPath + "\\" + "ScriptInfo.xml"); } //object to return var ob = { "Version" : 0, "UpdateUrl" : "", "PlscLocation" : "", "ReleaseNotes" : "", "DateReleased" : "", "Name" : "" }; //get the Update node values ob.UpdateUrl = xml.selectSingleNode("//Update/ScriptInfoUrl").text; ob.PlscLocation = xml.selectSingleNode("//Update/PlscLocation").text; ob.ReleaseNotes = xml.selectSingleNode("//Update/ReleaseNotes").text; ob.DateReleased = xml.selectSingleNode("//Update/DateReleased").text; ob.Name = xml.selectSingleNode("//Name").text; var version = xml.selectSingleNode("//Version").text; //convert the version to a real number, parseInt chops off the decimal points so we cheat and * it by 1 ob.Version = version * 1; return ob; //return our object }, "AskUser" : function(){}, "GetNewPlsc" : function(){} }
Now if you want to test the above code, add the following to your OnEvent_Initialize
Code:
function OnEvent_Initialize(){ var uc = new UpdateChecker(); }
Make sure you have a copy of your ScriptInfo.xml uploaded at the location specified at ScriptInfoUrl, otherwise the code will fail.
Now run the script, if your uploaded ScriptInfo.xml file has a greater version than the local ScriptInfo.xml it should display "Update is newer"
Ok, so now we need to do something if the update is newer, we will call the UpdateChecker.AskUser() method to ask the user if they want to update.
We will just display a messagebox with yes or no buttons, in your own script I recommend that you create a fancy GUI
Code:
"AskUser" : function(updateInfo){ //we will just display a simple yes/no messagebox, in your own version you could take this a step futher and display a pretty plus GUI for it, but thats outside the scope of this tutorial var message = "A new Script Update has been detected for " + updateInfo.Name + " Do you want to update?"; var result = Interop.Call("User32", "MessageBoxW", 0, message, "Script Update", 4 /* MB_YESNO */); if(result == 6){ //a result of 6 means that the user pressed "yes" this.GetNewPlsc(updateInfo.PlscLocation); //call the GetNewPlsc function to download the file } }
And finally we need to create the GetNewPlsc function to open the Plsc file.
Now in your own scripts you should use MsgPlus.DownloadFile to download the plsc and then use
Code:
new ActiveXObject("WScript.Shell").Run('"downloadlocation"');
to run it
but in this example we will simply execute the url
Code:
"GetNewPlsc" : function(url){ //function to download the plsc //execute the url new ActiveXObject("WScript.Shell").Run('"' + url + '"'); }
Ok so lets look at the code so far.
It should download the new ScriptInfo.xml and compare the versions, if theres an update then it will open the PlscLocation
Code:
var UpdateChecker = function(){ this.Check(); }; UpdateChecker.prototype = { "Check" : function(){ //This function will parse the local XML file and trigger the download the update ScriptInfo.xml var scriptInfo = this.ParseScriptInfo(); //will return an object in the form of {"Version" : 1.0, "UpdateUrl" : "http://.../scriptInfo.xml", "ReleaseNotes" : "", "DateReleased" : ""} this.details = scriptInfo; //add the details to the object, incase the script wants to use this for something Interop.Call("Wininet.dll", "DeleteUrlCacheEntryW", scriptInfo.UpdateUrl); //make sure theres no cached version of the update var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // Create a new XMLHttp request object to download our update ScriptInfo.xml // see http://msdn2.microsoft.com/en-us/library/ms536648.aspx for details on the open method xmlhttp.open("GET", scriptInfo.UpdateUrl, true) //open a new xmlhttp request to the updateUrl and set it to asynchronous operation var thisd = this; //create a reference to the current scope to use in the callback //see http://msdn2.microsoft.com/en-us/library/ms534308.aspx for more information on the onreadystatechange callback xmlhttp.onreadystatechange = function(){ //because this refers the function we have to use thisd when refering to our class if(xmlhttp.readyState == 4){ var updateInfo = thisd.ParseScriptInfo(xmlhttp.responseXML); //parses the update scriptInfo and returns an object if(updateInfo.Version > scriptInfo.Version){ //if version is newer Debug.Trace("Update is newer"); thisd.AskUser(updateInfo); }else{ Debug.Trace("Update Is older"); } } } xmlhttp.send(); }, "ParseScriptInfo" : function(xml){ //returns an object with the parsed ScriptInfo information if(typeof(xml) == "undefined"){ //if no xml is passed, we will load the local script info var xml = new ActiveXObject("Microsoft.XMLDOM"); xml.async = false; xml.load(MsgPlus.ScriptFilesPath + "\\" + "ScriptInfo.xml"); } //object to return var ob = { "Version" : 0, "UpdateUrl" : "", "PlscLocation" : "", "ReleaseNotes" : "", "DateReleased" : "", "Name" : "" }; //get the Update node values