Introduction
Office 365 is the next-generation cloud productivity service that delivers important functionalities for business, investing less than on-premise solution.
It brings together 2010 editions of Exchange Online, Lync Online, Office tools, and SharePoint Online as a cloud service for organizations of all sizes.
In particular SharePoint Online provides a solid business collaboration platform on which developers can build solutions by using standard development tools such
as Microsoft SharePoint Designer 2010 and Microsoft Visual Studio 2010. However,
from a developer point of view SharePoint Online puts many constraints and we need to modify the standard development
pattern.
We can only deploy sandboxed solutions that create a secured wrapper around webparts and other elements with some
limitation about SharePoint Object Model. They cannot absolutely modify or add any file to
the file system and all our code runs in separate worker processes and not in w3wp.exe.
This kind of solutions has pros (it doesn't require Application Pool Recycling!!) and cons (first of all it doesn't support Security Elevation). This article deals with
the issues that forbid us to deploy any WCF or "old" ASMX service using SharePoint Sandbox solutions.
The suggested solution overcomes this limitation, simulating a webservice with a simple ASPX page that returns data in JSON format rather than standard text/html.
In details we create:
- a sandbox webpart to encapsulate business logic of the service
- a custom master page to eliminate needless content in the output response
- a web page to simulate the webservice endpoint
- a web page to consume the service and show a typical use of it
Let's go!
Create Sandboxed webpart
It is not the focus of this article to create a complex business logic or show the capability of
the SharePoint Server-Side Object Model, but try to explain
a different approach to consume SharePoint data.
In the demo we are going to instantiate this simple object with some information about subsites:
public class Stats
{
public List<Site> Sites { get; set; }
}
public class Site
{
public string Title { get; set; }
public int ListsCount { get; set; }
}
The operation is simple using the SharePoint Object Model server-side: we do a for...each
iteration to get the title and the number of lists in the site; the same
operation in a client-side script could be more complex because of asynchronous
calls. For any details on these steps, I suggest to see the source code in
the attachment (WSWebpart feature)
After we fill the properties of the object, we have to serialize it and create the JSON string to send back to
the client. There are many way to obtain this, the most simple is to use JavaScriptSerializer
, included in .NET Framework 3.5 (in
the System.Web.Extensions.dll
assembly)
Stats stats = new Stats();
...
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(stats);
Custom Masterpage
The master page must be very simple in order to return only the raw result in JSON format. So we can create a master page with only
one placeholder:
<%@ Master language="C#" %>
<asp:ContentPlaceHolder id="PlaceHolderMain" runat="server">
</asp:ContentPlaceHolder>
In this way we have removed all unisexual information such as JavaScript, CSS, and other contents, obtaining a clean page.
Simple Service Page
Now we can add the custom sandbox webpart with the implementation of the service in a new page, forcing to use our master page. To achieve this, we can use a new control included in
the SharePoint 2010 framework: SPUserCodeWebPart
. The page on MSDN doesn't contain any example, however the required properties to
reference univocally the webpart are:
AssemblyFullName
: The full name of the user code Web Part assembly (e.g.
WebServiceSandbox, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bf13a1a9ef2c3dc4)SolutionId
: The ID of the solutionTypeFullName
: The full name of the sandboxed solution Web Part type
To simplify the process we can use the replaceable parameters included in Visual Studio 2010 solutions. In particular we have
$SharePoint.Project.AssemblyFullName$
for the
AssemblyFullName and
$SharePoint.Package.Id$
for the
SolutionId.
Instead for the
TypeFullName there isn't any specific variable, but as suggested in this detailed MSDN
article and in below comments, we can use the generic parameters
$SharePoint.Type.<GUID>.FullName$
replacing <GUID> with that added in
the WebPart declaration.
In the sample the code is:
<WebPartPages:SPUserCodeWebPart
runat="server"
Description="ServiceWP"
Title="ServiceWP"
AssemblyFullName="$SharePoint.Project.AssemblyFullName$"
SolutionId="$SharePoint.Package.Id$"
ID="servicewp"
TypeFullName="$SharePoint.Type.a2548058-31d8-4364-8043-1ea3a1eb21e1.FullName$" >
</WebPartPages:SPUserCodeWebPart>
Moreover in the declaration of the page we must change the URL of the MasterPage
in order to reference the custom one.
<%@ Page language="C#" MasterPageFile="~site/_catalogs/masterpage/WSMasterPage.master" ... %>
The ~site
parameter is required if the site collection isn't the root of the Web Application (e.g., http://dev/sites/wstest).
After we have created the page, we have to create a feature to deploy this in the SharePoint Document Library. This can be achieved using a module as this (in the solution it is in the
WSServicePage folder):
<Module Name="WSServicePage">
<File Path="WSServicePage\WSServicePage.aspx" Url="Documents/WSServicePage.aspx" />
</Module>
Consume webservice with jQuery
The last step is create a page consuming the service; it is quite simple,
there is a button that calls our custom webservice and displays the well-formed
result in a div
panel (with id #list
)
In order to make the development easy, we use AJAX functionalities of the jQuery
framework and we can insert the script in the OOTB SharePoint Content Editor
Webpart or add it directly in the source code of the page.
$(function () {
$.getJSON("/Documents/WSServicePage.aspx", function(data) {
$.each(data.Sites, function(i,s) {
var txt = 'Site ' + s.Title + ' has ' + s.ListsCount + ' lists' ;
$('<li>').text(txt).appendTo($('#list'));
});
});
});
Conclusion
In this article we have analyzed a way to overcome a Office 365 limitation: sandbox solutions can be difficult to use at times because of all the limitations, however there are many new techniques that allow developers to handle these issues exploring new approaches and solutions.
This approach doesn't want to replace other valid and totally supported solutions such as
JavaScript Client Object Model (JSOM),
SPServices, or simple calls to SharePoint REST services, instead it just aims to suggest a way to reuse source code developed with
a server-side object model or simply for users that haven't good practice with
JavaScript. Moreover I find this approach very useful when we need nested queries using SharePoint context that require many asynchronous calls using JSOM script.
About the sources
In the solution there are four features:
- WSWebpart with the webpart with the logic of service
- WSMasterPage with the simple masterpage
- WSService with the page containing Webpart
- WSConsumer with the page that calls the "service"
History
- 10th February, 2012: Initial version.
- 14th February, 2012: Updated source code.