Get Notified When O365 Tenancy Version Is Upgraded: Part 2, How The Code Works

Get Notified When O365 Tenancy Version Is Upgraded: Part 2, How The Code Works

Update: You can now download the Office 365 SharePoint Version Checker Add-in from the Microsoft Store.  Get it here.


Last week, I demonstrated how you can use a SharePoint-hosted App to notify you when your O365 tenancy is upgraded

This week I’m going to walk you through the code. 

Thanks for all the positive feedback regarding this approach, it’s great to hear so many folks believe this app will be helpful for them. 

Here’s another look at the flowchart which describes the logic the app uses.  Throughout this article I’ll break down each piece of the puzzle.

First, the app determines the app Web Url and it appends the _vti_pvt/service.cnf and _vti_pvt/buildversion.cnf suffixes to check for the Extender and Build versions, respectively.  The SPProductNumber version is obtained from the QueryString. 

Here’s what that looks like in JavaScript.  Notice the URL parsing is a little different when you are working with the app in your developer environment versus when you deploy it to an Office 365 tenancy.

context = SP.ClientContext.get_current();
    web = context.get_web();
    appWebUrl =

var appWebSlashLocation = appWebUrl.lastIndexOf("/");

spProductNumber =

//O365 Trim Logic
    trimmedAppWebUrl = appWebUrl.substring(0, 
     appWebUrl.lastIndexOf("") + 15);
    //Local Developer Site Trim Logic
    trimmedAppWebUrl = appWebUrl.substring(0, 

extenderVersionUrl = trimmedAppWebUrl + "/_vti_pvt/service.cnf";
    buildVersionUrl = trimmedAppWebUrl + "/_vti_pvt/buildversion.cnf";

Next, the app makes calls to the two URLs to obtain their payloads which include the Extender and Build versions and it displays the raw payloads. 

Then, the app parses the payloads to determine the version numbers.  Here’s what that JavaScript looks like.  

getVersion("extender", extenderVersionUrl);
getVersion("build", buildVersionUrl);

function getVersion(type, url) {

var request = $.ajax({
    url: url,
    type: 'GET',
    datatype: 'text'

request.done(function (data) {
    var properties = data.split("\r\n");
    for (t = 0; t < properties.length; t++) {
        if (properties[t].lastIndexOf("vti_" + type + "version:") == 0) {
        versionlocation = properties[t].lastIndexOf("vti_" + type +
        if (type == "extender") {
            extenderVersion = properties[t].substring(versionlocation +
        else {
            buildVersion = properties[t].substring(versionlocation + 17);

    if (type == "extender") {
    else {
    }); (msg) {

Error Status: ' + msg.status + '

Error Status Text: ' + msg.statusText + '

'); }); }

Notice the parsing pattern is a little bit fragile to grab the actual version number from the payloads the services return because it relies on string parsing. 

I tried to convert the return payload from the service into JSON and XML with the AJAX datatype attribute, but that had no effect.  I’ll continue to refactor this to make the code less fragile and post updates accordingly.

After the version numbers are known, the app queries the Versions list to see if the versions have already been logged.  If they have not been logged, it logs them to the Versions list and displays a message to indicate a new version was found and logged. 

I created three versions of the following JavaScript function in the app which perform this operation.  Each version of the JavaScript function correlates to a different version number (extender, build, SPProductNumber). 

To keep this post concise, I have only included the function which retrieves the SPProductNumber versions from the Versions list.

There’s nothing out of the ordinary in this code--it uses CSOM to execute a CAML query against the Versions list and registers the appropriate success and failure delegate functions. 

If you have not created CAML queries before, or even if you create them all the time, I recommend you download the CAML Designer tool to create them.  It saves you a ton of time creating your queries and lets you test your queries before you even plug them into your SharePoint apps.

function retrieveSPProductNumberVersionHistory(version) {
    var versionsList = context.get_web().get_lists().getByTitle('Versions');
    var camlQuery = new SP.CamlQuery();
    '' +
       ' +
    '' + version + '
    spProductNumberVersionsListItems = versionsList.getItems(camlQuery);
           'Include(Title, VersionType)');
    Function.createDelegate(this, this.onSpProductNumberQuerySucceeded),
    Function.createDelegate(this, this.onQueryFailed)

The onSpProductNumberQuerySucceededsuccess JavaScript delegate function evaluates if the version has already been logged.  If the version has not been logged, the addVersionListItem function is called to log the version to the Versions list. 

If the version has already been logged then the UI is updated with a message that indicates this is the case.

function onSpProductNumberQuerySucceeded(sender, args) {
    var spProductNumberListItemInfo = '';
    var spProductNumberEnumerator = 

while (spProductNumberEnumerator.moveNext()) {
    var spProductNumberListItem = spProductNumberEnumerator.get_current();
    spProductNumberListItemInfo += '\nVersion: ' + 
      spProductNumberListItem.get_item('Title') +
      '\nType: ' + spProductNumberListItem.get_item('VersionType');

if (spProductNumberListItemInfo == '') {
    addVersionListItem('SPProductNumber', spProductNumber);
    else {

SPProductNumber version has not changed.

'); $('#spProductNumberAlert').addClass('green'); } }

Although I did not try it in this sample, I’m willing to bet the parsing logic could be simplified if the REST API was used to query the Versions list instead of the CSOM API. 

I plan to refactor this code to see if that is indeed the case.  If the REST API makes parsing the results easier I’ll post an update in the future.

I’m still working on publishing the app to the marketplace--as soon as it is available, I’ll let you know.

Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.