SharePoint App Part (ClientWebPart, _WPID_) existence on host page

I was working on an internal SharePoint-Hosted app and had the need to create an interface that lists all app parts (SharePoint-Hosted app) added to SharePoint 2013 pages, but within the pages of the SharePoint-Hosted app itself. I had to use client side code (JavaScript).  

The idea was to enable users to configure the App Part instances through a centralised dashboard within the SharePoint-Hosted app itself. So users might have many App Parts added to pages, but a central page to configure them all.

In order for the dashboard to be accurate, an additional check for the app part existence on the host publishing/web page had to be performed before we can load all items on the dashboard UI. For that purpose I decided to use the LimitedWebPartManager and to check if app part is still present on particular SharePoint publishing/web page (It could be taken off).

One possible way to check if the app part exists (through the LimitedWebPartManager) is to have for example the SharePoint host url, the host page url that the app part resides and the app part id so we can execute code like on the example below and get the app part(ClientWebPart) object if exists or null if does not.


//C# CODE
using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SPSite site = new SPSite(<the SharePoint host url here>))  //ex.: "http://test.contosoapps.com"
            using (SPWeb web = site.OpenWeb())
            {
                SPFile file = web.GetFile(<the page url here>);    //ex.: "/SitePages/DevHome.aspx"
                SPLimitedWebPartManager lwpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
                SPLimitedWebPartCollection webParts = lwpm.WebParts;
                var webPart = webParts[<the webpart id here>];   //ex.: b8b45300-2d7e-4319-bdf3-0379ac8802f1

     if(webPart != null) {  //webpart exists }          
            }
        }
    }
}



//Client side code (JavaScript)
var siteUrl = 'http://test.contosoapps.com/';
            var hostPage = '/SitePages/DevHome.aspx';
            var webPartId = '49578EDC-E5D4-4FC2-91FF-1FD4D15C3C23'; //example
            var webPartObject = null;

            function retrieveWebPartObject() {

                var clientContext = new SP.ClientContext(siteUrl);
                var file = clientContext.get_web().getFileByServerRelativeUrl(hostPage);

                var limitedWebPartManager = file.getLimitedWebPartManager(SP.WebParts.PersonalizationScope.shared);
                webPartObject = limitedWebPartManager.get_webParts().getById('49578EDC-E5D4-4FC2-91FF-1FD4D15C3C23');
                clientContext.load(webPartObject);

                clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
            }

            function onQuerySucceeded(sender, args) {
                if (webPartObject != null) {
                    alert('The app part exists!');
                }
            }

            function onQueryFailed(sender, args) {
                alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
            }


In our case the client side code should be used.

Since we have the SPHostUrl available as a standard token we can get the SharePoint host url for our code. (external  reference: http://msdn.microsoft.com/en-us/library/jj163816.aspx)

The javascript document.referrer (external  reference:  https://developer.mozilla.org/en-US/docs/Web/API/document.referrer) can be used to define the host page url where our app part is added. As an external  reference on this page can be seen how to use the document.referrer http://blog.lekman.com/2013/03/automatically-resizing-app-parts-in.html. (In my case I have saved the document.referrer in a custom list item for further use.)

We only need the app part id to make the magic happen now.
However we do not have it within the app part under any form. The SharePoint-Hosted app does not know anything about the ClientWebPart ID for app part added on a SharePoint 2013 host page.

I did some research and I came across the additional app part tokens (external  reference: http://blog.sharepointalist.com/2013/02/sharepoint-2013-app-part-tokens.html). The additional SharePoint app part token _WPID_ (represents a WebPart.ID (sample value is "g_f5ac6d08_5b6f_41ea_90d1_0cc8a030061c")) can be used in our case. For some time I got confused that the _WPID_ is the ClientPartID, but my tests showed that these are two different things. Furthermore, I have looked into the Content DataBase just for curiosity to see what is happening in the AllWebParts table and it turned that the table knows about the _WPID_, but it is associated with the tp_WebPartIdProperty instead of the ID.

Velin Georgiev blog image

So it is there ...it exists , but how to get it by using the client side model.  

My first attempts to prove the app part existence trough the _WPID_ token instead of the ClienWebPartId were success, but I did it through C# code which wasn't my goal.


using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SPSite site = new SPSite("http://test.contosoapps.com"))
            using (SPWeb web = site.OpenWeb())
            {
                SPFile file = web.GetFile("/SitePages/DevHome.aspx");
                SPLimitedWebPartManager lwpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
                SPLimitedWebPartCollection webParts = lwpm.WebParts;
                var webPart = webParts["g_b8b45300_2d7e_4319_bdf3_0379ac8802f1"];
            }
        }
    }
}


With let's say blind and wild javascript debugging :) I succeeded to get it work with javascript as well. The limitedWebPartManager.get_webParts() collection object has method called getByControlId(). Unfortunately, the msdn reference of this (http://msdn.microsoft.com/en-us/library/jj245374.aspx) tells us nothing about it at the time I wrote this post :). However the below client side code is the complete solution to check if app part exists on host page by using the limitedWebPartManager within the SharePoint-Hosted app.


//JavaScript
var siteUrl = 'http://test.contosoapps.com/';  //the standart token SPHostUrl
            var hostPage = '/SitePages/DevHome.aspx';  //the document.referrer (JavaScript)
            var webPartId = 'g_b8b45300_2d7e_4319_bdf3_0379ac8802f8'; //the _WPID_ (SharePoint additional app part token)
            var webPartObject = null;

            function retrieveWebPartObject() {

                var clientContext = new SP.ClientContext(siteUrl);
                var file = clientContext.get_web().getFileByServerRelativeUrl(hostPage);

                var limitedWebPartManager = file.getLimitedWebPartManager(SP.WebParts.PersonalizationScope.shared);
                webPartObject = limitedWebPartManager.get_webParts().getByControlId(webPartId);
                clientContext.load(webPartObject);

                clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
            }

            function onQuerySucceeded(sender, args) {

                if (webPartObject != null) {
                    alert('The app part exists!');
                }
            }

            function onQueryFailed(sender, args) {

                alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
            }



Cheers, Velin

Comments