Calling SharePoint 2013 REST api using an AngularJS Service

Calling the SharePoint 2013’s REST api can be a repetitive task. During the last Office365 summit in Amsterdam, the developer track always pointed to the Office365 Patterns and Practices for samples on how to develop in office 365. To my greatest joy they also have an SPA sample!

AngularJS services provide a way to share code within the app. This is great for not always having to write the same code over and over again. In this case calling SharePoint REST urls using $http.

The following is a snippet from https://github.com/OfficeDev/PnP/blob/master/Samples/Core.SharePointProxyForSpaApps/SharePointProxyForSpaAppsWeb/App/common/services/sharepointproxy.service.js:

(function (angular) {
    "use strict";

    angular
        .module('sharepointproxy.service', [])
        .factory('SharepointProxyService', SharepointProxyService);

    function SharepointProxyService($http) {

        var proxyEndpointOptions = {
            url: 'api/sharepoint',
            method: 'POST',
            data: {}
        };

        var defaultProxyOptions = {

        };

        var service = {
            transformRequest: transformRequest,
            sendRequest: sendRequest,
            getWebTitle: getWebTitle
        };

        function getData(o) {
            return o.data;
        }

        function transformRequest(request) {
            var transformedRequest = proxyEndpointOptions;
            transformedRequest.data = request;

            return transformedRequest;
        }

        function sendRequest(request) {
            return $http(request)
                .then(getData)
                .then(function (data) {
                    return data;
                })
            ;
        }

        return service;
    }

    SharepointProxyService.$inject = ['$http'];

})(angular);

It’s a great example on how to start using an AngularJS service calling SharePoint. It can be easily extended with more defined methods, like getting the title of the hostWeb:

        function getWebTitle(hostWebUrl) {
            var requestUrl = hostWebUrl + '/_api/web/title';
            var request = {
                url: requestUrl,
                method: 'GET',
                headers: {
                    Accept: 'application/json;odata=verbose'
                }
            }
            var spRequest = transformRequest(request);
            return sendRequest(spRequest);
        }

All you need to add in your controller is a simple call to the service passing the hostWebUrl:

var queryParameterString = (window.location.search[0] === '?') ? window.location.search.slice(1) : window.location.search;
$scope.queryParameters = deparam(queryParameterString);

        $scope.requestServiceData = function () {
            console.log("Request service data!");
            var result = SharepointProxyService.getWebTitle($scope.queryParameters.SPHostUrl)
            .then(function (data) {
                $scope.serviceResponse = data;
            });
        };

Finish off by adding a button to your HTML to call the service:

<button ng-click="requestServiceData()">Request</button>
Advertisements

Variations.Current.UserAccessibleLabels can spawn lots of SPWebs

Be careful when using the OOTB SharePoint Publishing property Variations.Current.UserAccessibleLabels as it spawns SPWebs for every call.

To demo this I’ve created a web part and added 2 different code blocks to it with monitored scopes. The first one creates 3 objects with a direct reference to the UserAccessibleLabels property. The second one uses a local field which is only assigned once.

namespace TestVariations.TestAccessibleLabels
{
    [ToolboxItemAttribute(false)]
    public partial class TestAccessibleLabels : WebPart
    {
        private ReadOnlyCollection<VariationLabel> userAccessibleLables = null;

        public ReadOnlyCollection<VariationLabel> UserAccessibleLables
        {
            get
            {
                if (userAccessibleLables == null)
                    userAccessibleLables = Variations.Current.UserAccessibleLabels;

                return userAccessibleLables;
            }
        }

        public TestAccessibleLabels()
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                var resultBuilder = new StringBuilder();

                using (new SPMonitoredScope("Variations.Current.UserAccessibleLabels"))
                {
                    var variationLabels = Variations.Current.UserAccessibleLabels;
                    var variationLabels2 = Variations.Current.UserAccessibleLabels;
                    var variationLabels3 = Variations.Current.UserAccessibleLabels;

                    foreach (var label in variationLabels)
                    {
                        resultBuilder.Append(label.Title);
                    }

                    foreach (var label2 in variationLabels2)
                    {
                        resultBuilder.Append(label2.Title);
                    }

                    foreach (var label3 in variationLabels3)
                    {
                        resultBuilder.Append(label3.Title);
                    }
                }

                using (new SPMonitoredScope("this.UserAccessibleLabels"))
                {
                    var variationLabels4 = this.UserAccessibleLables;
                    var variationLabels5 = this.UserAccessibleLables;
                    var variationLabels6 = this.UserAccessibleLables;

                    foreach (var label4 in variationLabels4)
                    {
                        resultBuilder.Append(label4.Title);
                    }

                    foreach (var label5 in variationLabels5)
                    {
                        resultBuilder.Append(label5.Title);
                    }

                    foreach (var label6 in variationLabels6)
                    {
                        resultBuilder.Append(label6.Title);
                    }
                }

                resultLabel.Text = resultBuilder.ToString();
            }
            catch (Exception ex)
            {
                resultLabel.Text = ex.Message;
            }
        }
    }
}

If we take a look at the developer dashboard, we can see SPWebs getting generated for each variation:

spweb spawns

Using ILSpy we can see that SharePoint opens web objects to check the current user’s access.

ilspy variations

When taking a look at the loading time, we see that the impact on the total loading time is quite high when using lots of direct references.

spweb monitored scopes

For the conclusion we can say that using the OOTB SharePoint property is the way to go when you only have to use it once. However when you’re using this property all over the place in your code it’s best to keep the “result” of the UserAccessibleLabels in a private field.

PageLayouts not updating through module

Trying to deploy an updated page layout through a module to the master page gallery was going wrong horribly. The page layout inside the gallery wasn’t updating while deployment was succesful and nothing was showing up in the logs as of why there wasn’t an update done to the file. The project as example looks like this:

pagelayoutproject

As you can see it’s a simple module which will deploy “testLayout.aspx” to the master page gallery using the following elements.xml:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="PageLayouts" Url="_catalogs/masterpage" RootWebOnly="TRUE">
    <File Path="PageLayouts\testLayout.aspx" Url="PageLayouts/testLayout.aspx" Type="GhostableInLibrary">
      <Property Name="Title" Value="My Custom Page Layout" />
      <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
      <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png" />
      <Property Name="PublishingAssociatedContentType" Value=";#$Resources:cmscore,contenttype_articlepage_name;;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#"/>
    </File>
  </Module>
</Elements>

The page layout was generated through SharePoint Designer and had the following source:

<%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>

<%@ Register TagPrefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:content contentplaceholderid="PlaceHolderPageTitle" runat="server">
	<SharePointWebControls:FieldValue id="PageTitle" FieldName="Title" runat="server"/>
</asp:content>
<asp:content contentplaceholderid="PlaceHolderMain" runat="server">
</asp:content>

Going deeper into the page’s properties it showed that the page has been customized, which was odd since nobody touched the file through SharePoint designer which would cause ghosting.

customizedpagelayout

After some research I’ve found a lot of suggestions to go the code way of manually removing the file and then add the page layout, but in my opinion it should be done through the module without code. I knew it was possible through a simple update of the module so what was going wrong? Easy: SharePoint Designer. If you look closely, the tool adds the following meta tags to the Page tag:

meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document"

For some reason, whenever these tags are there, SharePoint will always ghost your page layouts no matter what. Removing these tags and unghosting the page was sufficient to get back those updates through modules. Phew!

Updating content types using SharePoint Web Services (SPServices)

When you deploy an updated content type which is created from code and have new fields, your changes won’t automatically be pushed to children that inherit from your content type. This is only the case when you have created your content type using code. In the sandbox solution, I’m using the SharePoint Web Services to add new fields to content types that inherit from the updated content type.

Why the web services? With the push from Microsoft to go more and more client side, I found it a challenge to not do something with simply a farm solution or a server-side script. During the development of the web part, I’ve found 2 annoyances by using the SPServices library and jQuery/SharePoint:

  1. SPServices says every method name is unique. This is not true. There’s a method “UpdateContentType” for both the Lists.asmx and the Webs.asmx. Due to this overlooked bug, the call will always be directed to the Webs.asmx web service. I’ve modified the SPServices js file to comment out the WSops.UpdateContentType method for Webs.
  2. jQuery will always put your generated attributes to lower case. The web services are case-sensitive as you can see from the following screenshot (taken from STSSOAP.DLL, decompiled with ILSpy)
    caseSensitive

The web part itself is pretty straight forward. You’ll get a list of content types with update links.

content type updater webpart

When using firebug or another web dev tool, you can track all the calls that are made to the Webs.asmx & Lists.asmx

content type updater console window

I’ve deployed a custom content type to my site, created a list with that content type and then made an update to the site content type. As you can see there’s a column missing from the list content type:

site content type

list content type

After I click on “Update” next to “CustomContentType”, the script will detect a difference between these two and update accordingly:

soap call new fields

And the updated list content type:

updated list content type

All in all I’m pretty happy with the result. You can do a lot with just the web services from SharePoint, but they take some time to get used to. Also the documentation isn’t always great as for instance they mention to use <FieldRefs> while in the source code of the web service itself there’s only a parse of the <Fields> tag.

For the full source, or if you want to download and test the web part itself, you can go to CodePlex.

Create list views across webs using PowerShell or code

Sometimes you’ll want to have one basic list, which is accessible on all your subsites. One way to do this is using the content query web part. This however, blocks the functionalities a standard list view offers. So instead of the content query web part, we’ll use the basic XsltListViewWebPart. An important note: the standard ListViewWebPart class cannot work across your different webs inside your site collection. You have to use the Xslt one.

First, get an instance of the list you want to reference in your subsite:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]

Then, get the web part manager of the page of your subsite where you want to insert the list view:

$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);

Now that we’ve got both required objects, it’s time to create the XsltListViewWebPart:

$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id

It’s very important the WebId is set or otherwise you’ll get a nasty error like this:

“List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user.”

Last but not least, add the web part to your page:

$wpm.AddWebPart($ListViewWebPart, "Left", 0)

In one flow the script looks like this:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]
$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);
$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id
$wpm.AddWebPart($ListViewWebPart, "Left", 0)

If you would like for instance feature stapling, you can use the following code block to your web feature activation:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    using (var rootWeb = web.Site.OpenWeb())
    {
        var list = rootWeb.Lists[Constants.ListName];
        string file = web.RootFolder.WelcomePage;
        var webPartManager = web.GetLimitedWebPartManager(file, PersonalizationScope.Shared);
        var webPart = new XsltListViewWebPart
        {
            ListId = list.ID,
            Title = list.Title,
            WebId = list.ParentWeb.ID
        };
        webPartManager.AddWebPart(webPart, "Left", 0);
    }
}

Check if an item has been previously approved and approve again

Sometimes, you’ll have to check in an event receiver if an item has been previously approved and then do an action.

The following extension will return a boolean wether or not the item has been approved in the past or not.

public static bool HasBeenPreviouslyApproved(this SPListItem item)
{
    bool hasBeenPreviouslyApproved = false;
    if (item.Versions.Count > 1)
    {
        var prevPublishedVer = item.Versions.Cast<SPListItemVersion>().Where(v => v.Level == SPFileLevel.Published).LastOrDefault();
        if (prevPublishedVer != null)
        {
            hasBeenPreviouslyApproved = true;
        }
    }
    return hasBeenPreviouslyApproved;
}

As an example you can put the state back to approved if a modification was made. By default this always goes back to pending when an item gets edited, but in some cases you want to keep the state to approved.

if (properties.ListItem.HasBeenPreviouslyApproved())
{
    using (var web = properties.OpenWeb())
    {
        this.EventFiringEnabled = false;
        properties.ListItem.ModerationInformation.Status = SPModerationStatusType.Approved;
        properties.ListItem.SystemUpdate(false);
        this.EventFiringEnabled = true;
    }
}

Please note that when you use this code in an event receiver, it only works in the ItemUpdated event.

As a result, an announcement which was once approved, will always be approved, until a content editor decides to change the approval status back to pending or decline.

announcement approved

Update SharePoint Timer Job’s progress bar – the easy way

Every time I develop a timer job, I always add a progress bar. Well, if the job runs long enough that is. It adds that bit more refinement to the whole project and it’s pleasant to see your job crunching through the numbers.

For this example I’ve set up a standard timer job project:

timer job project structure

  • A Web Application scoped feature which installs the timer job
  • A static class ProgressUpdater which will handle the timer job’s progress updating
  • A class service which will do something
  • A class TimerJob which is our core job definition

I’ve chosen to add a class service to demonstrate the update process from outside the JobDefinition scope.

For the timer job I have the following code:

using Microsoft.SharePoint.Administration;
using System;

namespace SharePointTimerJob
{
    public class TimerJob : SPJobDefinition
    {
        private const string JOBNAME = "Custom Timer Job";

        public TimerJob()
            : base()
        {
        }

        public TimerJob(SPWebApplication webApplication)

            : base(JOBNAME, webApplication, null, SPJobLockType.Job)
        {
            this.Title = JOBNAME;
        }

        public TimerJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)

            : base(jobName, service, server, targetType)
        {
        }

        public static string JobName
        {
            get { return JOBNAME; }
        }

        public override void Execute(Guid contentDbId)
        {
            ProgressUpdater.Job = this;

            var service = new Service();
            service.Process();
        }
    }
}

As you can see it’s pretty straight forward. One note here is the ProgressUpdater.Job = this; line. This sets the Job property of the ProgressUpdater class to the executing instance.

The next piece of code is the ProgressUpdater:

namespace SharePointTimerJob
{
    public static class ProgressUpdater
    {
        public static TimerJob Job
        {
            get;
            set;
        }

        public static void Update(int percentage)
        {
            if (Job == null)
            {
                // throw or return
                return;
            }

            if (percentage >= 0 && percentage <= 100)
                Job.UpdateProgress(percentage);
        }
    }
}

And the code of the service to trigger an Update call:

using System.Threading;

namespace SharePointTimerJob
{
    public class Service
    {
        public void Process()
        {
            int[] collection = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            int count = 0;
            foreach (var item in collection)
            {
                Thread.Sleep(5000);
                count++;
                ProgressUpdater.Update((count * 100) / collection.Length);
            }
        }
    }
}

All this does is sleep the thread and update afterwards, but you get the idea.

When you run the timer job you’ll see that the progress updates nicely in ticks of 10% because of the collection, which is 10 items:

progress 10

progress 40