Friday, August 5, 2011

Enumerate sites using javascript client object model in Sharepoint

Recently I needed to enumerate all sub sites of the root site using javascript client model. As you probably know in order to perform calls using client object model in Sharepoint you need to create context, assign method call result to some local variable and then call load() method in order to get the data from Sharepoint. Looks easy. However there were several problems I would like to share.

First of all you are not able to use Sharepoint client object model in separate ASP.Net web application outside of the Sharepoint (because of cross-site scripting) – you can use it e.g. in application layouts page in context of some Sharepoint site: http://example.com/_layouts/foo.aspx. Also using javascript object model you can work only with site in which context you opened the page (in example above it is http://example.com). Other sites can not be accessed (also because of cross-site scripting). However even if you load the page in context of Sharepoint site you need to reference correct javascript files. As you probably know javascript client object model is defined in sp.js file in 14/Layouts folder. However if you will try to reference it using script tag:

   1: <script type="text/javascript" src="/_layouts/sp.js"></script>

you may get “Type is undefined” error. Then you can try to use ScriptManager and reference scripts using ScriptReference, or ScriptLink also without success. You may try to add additional scripts explicitly: 1033/init.js, 1033/core.js, sp.runtime.js, etc. and continue to get unclear javascript errors. Example which worked for me can be found here: Setting Up an Application Page for JavaScript. However you need to delete the following links in order to get it work:

   1: <script type="text/ecmascript" src="/_layouts/SP.Core.js" />
   2: <script type="text/ecmascript" src="/_layouts/SP.Debug.js" />
   3: <script type="text/ecmascript" src="/_layouts/SP.Runtime.Debug.js" />

Also you may want to change custom codebehind class to standard one:

   1: <%@ Page Language="C#" MasterPageFile="~/_layouts/applicationv4.master" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>

After that page should be loaded without javascript errors. Let’s now return to exact post topic: enumerate sites using client object model. There is OTB SP.WebCollection class for this. What method you need for any collection? Yes, method which returns total number of items in collection. Interesting that msdn doesn’t say anything about it: neither in methods nor in properties. However this method exists: get_count() (I found it by enumerating properties and methods using “for (w in webs)” statement).

The final code for the page is the following:

   1: <%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
   2: <%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
   3: <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
   4: <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" 
   5:   Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   6: <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" 
   7:   Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   8: <%@ Register Tagprefix="asp" Namespace="System.Web.UI" 
   9:   Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
  10: <%@ Import Namespace="Microsoft.SharePoint" %>
  11: <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  12: <%@ Page Language="C#" MasterPageFile="~/_layouts/applicationv4.master" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>
  13:  
  14: <asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
  15:  
  16: <script type="text/javascript">   1: 
  17:     function onWebsLoaded(sender, args) {
  18:         for (var i = 0; i < this.webs.get_count(); i++) {
  19:             alert(this.webs.itemAt(i).get_title());
  20:         }
  21:     }
  22:  
  23:     function onWebLoaded(sender, args) {
  24:         var clientContext = new SP.ClientContext.get_current();
  25:  
  26:         this.webs = this.oWebsite.get_webs();
  27:         clientContext.load(this.webs);
  28:         clientContext.executeQueryAsync(Function.createDelegate(this, this.onWebsLoaded), Function.createDelegate(this, this.onQueryFailed));
  29:     }
  30:  
  31:     function onQueryFailed(sender, args) {
  32:         alert('request failed ' + args.get_message() + '\n' + args.get_stackTrace());
  33:     }
  34:  
  35:     function retrieveWebSites() {
  36:         var clientContext = new SP.ClientContext.get_current();
  37:         this.oWebsite = clientContext.get_web();
  38:         clientContext.load(this.oWebsite);
  39:         clientContext.executeQueryAsync(Function.createDelegate(this, this.onWebLoaded), Function.createDelegate(this, this.onQueryFailed));
  40:     }
  41: </asp:Content>
  42:  
  43: <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
  44:  
  45:     <SharePoint:FormDigest ID="FormDigest1" runat="server"></SharePoint:FormDigest>
  46:  
  47:     <input type="button" id="btn" value="Show all webs" OnClick="retrieveWebSites()" />
  48:  
  49: </asp:Content>

So we added a button “btn” and handler “retrieveWebSites” for onclick event. In this handler we open context and load root web site (lines 36-39). In the onSuccess handler we load sub sites and in second onSuccess handler we enumerate all sites using get_count() method.

Hope it will help you with understanding of client object model.

No comments:

Post a Comment