Fixing the SharePoint Napa Tutorial to Read Data From Your Own Site

Office 365 provides a set of online coding tools for building SharePoint Apps called “Napa”.  The Napa tools provide a web based coding environment for building custom Office apps including SharePoint Apps.  These apps can be deployed to a testing site and then published to your app catalogue for use by your end users.

The basic Napa tutorial provides an example of how to read, create and delete SharePoint lists. 

If you code the basic JavaScript provided and run the app, you will see the following results:

image

The list of lists is coming from the context of the App, not the original SharePoint site that called the app.  This is why if you create a new list through the app, you won’t be able to easily find it. 

This is clearly not what we would want in the real world – we would want our app to interrogate the SharePoint site in which our app resides, not some artificial application context!  How do we get this to work?

After researching a few message boards, blogs and tutorials, I found a few bits of code that will help.  The following code is the same basic Napa application but with the context set to the host SharePoint site.  Below is the explanation as to what is going and how to create and publish the app.

//comment strict out because will not work with IE10
 // 'use strict';

//define the variables
     var hostUrl 
     var context
     var hostcontext
     var web
     var user
     var message = ""
     var lists

 //make sure we get SharePoint Ready after the browser DOM is ready
 $(document).ready(function () {
     SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
     $("#getListCount").click(function (event) {
        getWebProperties();
        event.preventDefault();
    });

    $("#createlistbutton").click(function (event) {
        createlist();
        event.preventDefault();
    });

    $("#deletelistbutton").click(function (event) {
        deletelist();
        event.preventDefault();
    });

 });

// This function creates a context object which is needed to use the SharePoint object model
 function sharePointReady() {
     var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
     context = new SP.ClientContext.get_current();
     hostcontext = new SP.AppContextSite(context, hostUrl);
     web = hostcontext.get_web();
     loadWebTitle();
     loadUserName();
     displayLists();
 }

function onGetWebSuccess() {
    message = message + "The title of the host web of this app is " + web.get_title() + ". ";
    updateMessage();
}
 
function loadWebTitle()
{
    context.load(web, "Title");
    context.executeQueryAsync(onGetWebSuccess, onGetWebFail);
}
 
function updateMessage()
{
    $('#message').text(message);
}
 
 // This function prepares, loads, and then executes a SharePoint query to get the current users information
function loadUserName() {
     user = web.get_currentUser();
     context.load(user);
     context.executeQueryAsync(onGetUserNameSuccess, onGetUserNameFail);
}

// This function is executed if the above call is successful
 // It replaces the contents of the 'helloString' element with the user name
function onGetUserNameSuccess() {
    message = message + "Hello " + user.get_title() + ". ";
    updateMessage();
}

 // This function is executed if the above call fails
function onGetUserNameFail(sender, args) {
    alert('Failed to get user name. Error:' + args.get_message());
}

function onGetWebFail(sender, args) {
    alert('Failed to get lists. Error:' + args.get_message());
}

function getQueryStringParameter(param) {
     var params = document.URL.split("?")[1].split("&");
     //var strParams = "";     
     for (var i = 0; i < params.length; i = i + 1) {
         var singleParam = params[i].split("=");
         if (singleParam[0] == param) {
             return singleParam[1];
         }
     }
}
    
function getWebProperties() {
    // Get the number of lists in the current web.
    context.load(lists);
    context.executeQueryAsync(onWebPropsSuccess, onWebPropsFail);
}

function onWebPropsSuccess(sender, args) {
    alert('Number of lists in web: ' + lists.get_count());
}

function onWebPropsFail(sender, args) {
    alert('Failed to get list. Error: ' + args.get_message());
}

function displayLists() {
    // Get the available SharePoint lists, and then set them into 
    // the context.
    lists = web.get_lists();
    context.load(lists);
    context.executeQueryAsync(onGetListsSuccess, onGetListsFail);
}

function onGetListsSuccess(sender, args) {
    // Success getting the lists. Set references to the list 
    // elements and the list of available lists.
    var listEnumerator = lists.getEnumerator();
    var selectListBox = document.getElementById("selectlistbox");
    if (selectListBox.hasChildNodes()) {
        while (selectListBox.childNodes.length >= 1) {
            selectListBox.removeChild(selectListBox.firstChild);
        }
    }
    // Traverse the elements of the collection, and load the name of    
    // each list into the dropdown list box.
    while (listEnumerator.moveNext()) {
        var selectOption = document.createElement("option");
        selectOption.value = listEnumerator.get_current().get_title();
        selectOption.innerHTML = listEnumerator.get_current().get_title();
        selectListBox.appendChild(selectOption);
    }
}

function onGetListsFail(sender, args) {
    // Lists couldn’t be loaded - display error.
    alert('Failed to get list. Error: ' + args.get_message());
}

function createlist() {
    // Create a generic SharePoint list with the name that the user specifies.
    var listCreationInfo = new SP.ListCreationInformation();
    var listTitle = document.getElementById("createlistbox").value;
    listCreationInfo.set_title(listTitle);
    listCreationInfo.set_templateType(SP.ListTemplateType.genericList);
    lists = web.get_lists();
    var newList = lists.add(listCreationInfo);
    context.load(newList);
    context.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}

function onListCreationSuccess() {
    displayLists();
}

function onListCreationFail(sender, args) {
    alert('Failed to create the list. ' + args.get_message());
}

function deletelist() {
    // Delete the list that the user specifies.
    var selectListBox = document.getElementById("selectlistbox");
    var selectedListTitle = selectListBox.value;
    var selectedList = web.get_lists().getByTitle(selectedListTitle);
    selectedList.deleteObject();
    context.executeQueryAsync(onDeleteListSuccess, onDeleteListFail);
}

function onDeleteListSuccess() {
    displayLists();
}

function onDeleteListFail(sender, args) {
    alert('Failed to delete the list. ' + args.get_message());
}

 

Setting the Context to the Host App

The key difference in approach in this version of the app is the setting of the context to be the host URL.  The host URL is stored in the query string passed into the application and is decoded using the function decodeURIComponent.

var hostUrl = decodeURIComponent(getQueryStringParameter(“SPHostUrl”));

We then reset the context to be from the hostURL instead of the original application site context:

hostcontext = new SP.AppContextSite(context, hostUrl);
web = hostcontext.get_web();

Testing the Basic Context

We created two basic methods to test that we have the context set appropriately.  loadWebTitle() grabs the title of the web and loadUserName() grabs the current user name. 

The Rest of the Original Napa App

The original Napa tutorial application displays all the SharePoint lists in the current web context.  This now works but reflects our new context based on the host SharePoint site.

Setting the Right Permissions

Now that we are reading list information from the host app, we need to request the appropriate permissions from the host app.  Under the settings there is a permissions tab that allows you to set up the right permissions to request when deploying your app.

image

In order to read the lists, the Web permission needs to be set to “Read”.  In order to delete a list, the Web permission needs to be set to “Manage”. 

When you deploy your app to your site, you will be asked whether you trust your app and will be shown the permissions being requested.

image

The Result

Here is the result based on my development site.  As you can see, it now shows all the lists in my site. 

image

In addition, if I create a new list it now shows up as expected in the host site.

image

image

  • wayback007

    You absolutely saved my sanity. I think there’s something wrong with the sample code in the original MS blog. I followed it step by step but it never runs. I cut and pasted your’s in and it ran right away. So hopefully now I can get on with this.

    Jerry

  • Amit Lohogaonkar

    you are my hero! saved my weekend work!! Thanks