Kentico 8.1 Technology – Using SharePoint API
Kentico 8.1 features a brand new implementation of SharePoint integration. The main purpose was to make the developers’ lives easier and to provide you with the ability to retrieve data from a SharePoint server without the hassle. In this article, we will focus on how to use the API for the purposes of customization, specifically when the supplied SharePoint data source web part is not enough.
Kentico 8.1 allows you to integrate SharePoint in versions 2010, 2013 or Online on your Kentico site. All you have to do is reference our SharePoint library in your Visual Studio project and continue reading.
SharePoint services
SharePoint integration is logically divided into services – each of them is responsible for a portion of functions and is described by its own interface. For each supported SharePoint version there is a family of classes implementing these services. Basically, SharePoint integration was inspired by the abstract factory design pattern – this may not be evident since it is neatly tied together in order to provide you straightforward access to any service.
We have
ISharePointSiteService,
ISharePointListService and
ISharePointFileService. The latter two services, in particular, will come in handy. But for now, let’s take a look at a simple piece of code that demonstrates the desired service instance retrieval.
Let’s say that we would like to test whether the server instance is accessible. The
ISharePointSiteService is well-suited for our purposes. The following code snippet demonstrates how to use it:
// connectionData is instance of SharePointConnectionData
try
{
ISharePointSiteService siteService = SharePointServices.GetService<ISharePointSiteService>(connectionData);
string site = siteService.GetSiteUrl();
}
catch...
For the sake of briefly describing what it does,
GetService locates the correct family of classes based on the version provided via
connectionData and gets you requested service implementation.
The catch clause is important here since we have to take care of all possible error conditions met in the process of site URL retrieval (unavailable SharePoint server, invalid credentials or unsupported authentication mode, etc.).
SharePoint connection data
The lines of code seen above make use of the
SharePointConnectionData class. This is an envelope for all data needed to successfully connect to the SharePoint server instance. It contains typical properties like
SharePointConnectionSharePointVersion,
SharePointConnectionAuthMode etc. But you can add and access your own configuration items via the
[] operator.
Those of you familiar with Kentico info objects might be wondering if they fit in here. Of course they do! When your users define their connection in the
SharePoint application within the UI, they actually work with
SharePointConnectionInfo. And when you have this type of info object describing your server, you can supply it to the
ISharePointServiceFactory by calling the
ToSharePointConnectionData method.
To clarify, there is a need for a
SharePointConnectionData class instead of direct usage of an info object – we wanted a lightweight encapsulation of all necessary configuration data. Since every service implementation holds its own instance of connection data (to make sure it is not unintentionally modified from any external code), the overhead has to be minimized. When making your own service implementation, you should follow this practice and keep a clone of the connection data unless you are 100 percent sure no one from outside will modify it. The
Clone method of the connection data class performs a shallow copy of all items specifically for this purpose.
Interacting with SharePoint lists
One of the main reasons why you should integrate with SharePoint is the necessity to retrieve data stored in SharePoint lists. Just to be clear, when we use the word
list we mean
list or library, which are very similar on the implementation level.
When you need to retrieve all lists of a certain type available on a server, the
GetLists method of the
ISharePointListService is the best choice – it takes one method argument to specify the list type that has to be retrieved. SharePoint recognizes various types of lists – we have chosen the ones that we consider to be most important. You can find them in the
SharePointListType class. They can all be used for any supported SharePoint server versions. If you are in need of any other list type just consult Microsoft’s documentation (look for
list template type) so that you get the right type code.
DataSet lists = listService.GetLists(SharePointListType.PICTURE_LIBRARY);
The snippet above retrieves all picture libraries within the server. The
listService has been obtained in the same manner as the
siteService in the previous example. The return type has been chosen with respect to easy binding to repeater controls or grids.
SharePoint list items
It’s good to know that the retrieval of lists works; but in real-life scenarios you may be more interested in the actual data items that are present within a list. This is where the
GetListItems method comes into play. It contains more arguments than the previous one – we will explain them in further detail after we take a look at the following snippet of code:
// connectionInfo is instance of SharePointConnectionInfo
ISharePointListService listService = SharePointServices.GetService<ISharePointListService>(connectionInfo.ToSharePointConnectionData());
string listTitle = "Company parties";
string folderServerRelativeUrl = "/Company parties/Christmas/2013";
List<string> viewFields = new List<string> { "ID", "FileRef", "FileLeafRef", "Title", "ImageWidth", "ImageHeight", "Keywords" };
int imageId = imageIdFromQueryString; // passed as method parameter
SharePointListItemsSelection selection = null;
if (imageId > 0)
{
selection = new SharePointListItemsSelection
{
FieldName = "ID",
FieldType = "Counter",
FieldValue = imageId.ToString()
};
}
SharePointView view = new SharePointView
{
Scope = SharePointViewScope.FILES_ONLY,
QueryInnerXml = "<OrderBy><FieldRef Name=\"ID\" Ascending=\"true\"/></OrderBy>",
ViewFields = viewFields
};
DataSet result = listService.GetListItems(listTitle, folderServerRelativeUrl, view, selection);
In the retrieval of the
ISharePointListService instance, you can see the conversion of the info object to the connection data. The
listTitle is the readable,
human form of the list’s title. The
folderServerRelativeUrl we are using assumes that we have a SharePoint
picture library called
Company parties that has a folder structure containing some photos. We are restricting the retrieved fields (
viewFields) from the SharePoint server only to those we are interested in. It’s good practice to ask only for the data you really need. Please note that the SharePoint server will always pass along some system fields that cannot be excluded from transmission. The field names are actually the
internal names used by the server.
To select any given picture from the SharePoint picture library (based on the ID), you must define a selection. A selection is defined by a field name (internal name), a data type of the field and a value. The value is used in equality comparison. To get familiar with
list data types you can have a look at the SharePoint server documentation. To perform a valid selection all three properties have to be specified. Otherwise, it will be ignored.
Last but not least, construct the
SharePointView. The scope limits retrieved items to files only – any subfolders in our
Christmas/2013 folder will not be included in the result. The
QueryInnerXml is the raw content of the CAML Query element issued to the SharePoint server. If you are not familiar with the query syntax, see the
Query documentation. Currently, SharePoint Online is hosted on the 2013 edition. Be aware that any custom queries that are using special features may not be independent from the SharePoint version.
Important: the properties forming the selection via
SharePointListItemsSelection are safely encoded. However, the
QueryInnerXml of the SharePointView inherently cannot be encoded, so be careful when processing a user defined input to prevent CAML query injection.
How the selection is made
Those of you with knowledge of SharePoint querying might be curious about the selection and how it was made. The selection uses equality comparison, and supplements the where condition provided via
QueryInnerXml in the form of a logical conjunction – this was put in place to make it more comfortable to use.
Retrieving SharePoint files
In the last section devoted to SharePoint data retrieval, we will focus on binary data stored on the server. You may already sense that we are going to utilize the
ISharePointFileService. First, we have to get a reference to the file as follows:
string fileRef = "/Company parties/Christmas/2013/IMG_0001.jpg";
ISharePointFileService fileService = SharePointServices.GetService<ISharePointFileService>(connectionData);
ISharePointFile file = fileService.GetFile(fileRef);
The
fileRef contains a value from the picture library field called
FileRef. This is a default column for the SharePoint picture libraries’ binary content, but your custom-named columns can contain binary data as well. The lines above do not actually produce a request to the server. Rather, the provided ISharePointFileService uses lazy loading.
Accessing binary content
The
ISharePointFile introduces two different approaches to binary content retrieval – all bytes at once or a stream. When you need to process a file on the application server (i.e. image resizing), the first available option is the correct one:
byte[] binaryFileContent = file.GetContentBytes();
The advantage here is simplicity – but when working with larger files their retrieval to an allocated array of bytes can take up a lot of memory. So when you do not actually need to process the data as a whole, you should obtain a reference to the byte stream and then forward the bytes via some buffer directly to the HTTP response stream (if sending to client is what you desire):
using (Stream fileStream = file.GetContentStream())
{
// Read the stream to buffer
// Do some on-the-fly processing (optional)
// Flush the buffer to the HTTP response stream
}
Accessing metadata
SharePoint stores some metadata along the binary file content. When you want to use any kind of metadata, you can use the following code:
string title = file.Title;
string etag = file.ETag;
long length = (file.IsLengthSupported) ? file.Length : -1;
DateTime? ceated = file.TimeCreated;
Once read, the metadata properties will be retrieved all at once. Since SharePoint 2010 does not include support for the
length property, you should access the property’s content conditionally when writing a version-independent code.
Registering custom SharePoint service factory
The day may come when you are in need of your very own service factory. The reasons for doing this may vary, but the way to do so is still the same. First, you have to prepare your implementation of
ISharePointServiceFactory. By examining the interface’s methods, you will find out that the only one you need to implement is:
IService GetImplementationFor<IService>(SharePointConnectionData connectionData)
where IService : ISharePointService;
The name of the method speaks for itself. Based on the requested service kind and connection data, you have to return a suitable service implementation instance. Or simply throw
SharePointServiceNotSupportedException to indicate the inability to satisfy the request.
Now you have to let the system know about your service implementation. This can be achieved by simply annotating your assembly in the following manner:
[assembly: RegisterSharePointServiceFactory("sharePoint2140", typeof(SharePoint2140ServiceFactory)]
public class SharePoint2140ServiceFactory : ISharePointServiceFactory
{
public IService GetImplementationFor<IService>(SharePointConnectionData connectionData)
where IService : ISharePointService
{
...
}
}
The
RegisterSharePointServiceFactory attribute performs all the necessary work for you.
You may also want to introduce your custom services within the service family served by your factory. If this is the case, you ought to inherit the
ISharePointService interface when declaring your own service’s interface.
Good programming habits
In the examples shown above we have omitted the try-catch clauses to make the code shorter and easier to read – but you should definitely avoid doing that in your own production code, unless you want your users to meet some uncaught exceptions.
Also note that the services offer a direct SharePoint server querying, which can be rather costly operation. Therefore, you should consider some caching mechanism that is suitable for you. The SharePoint data source web part has the ability to cache the retrieved data out of the box.
Conclusion
To sum it all up, when accessing SharePoint data the following steps are necessary:
-
Pick the right ISharePointService to be instantiated by a service factory and retrieve it by calling SharePointServices.GetService.
-
Use the service as described in the pieces of code shown in the article.
-
Be aware of any possible exceptions.