U2U Blog

for developers and other creative minds

TextBox

Using the Workflow Definition Language in Microsoft Flow

I’ve been playing with Microsoft PowerApps and Microsoft Flow since the first time it was introduced. And I must admit, I really love the way you can create mobile apps and flows in just a couple of minutes. I agree, once you really get in there, you bump into things that are still missing. But hey, that just means there is still room for improvement …

What I would like to show in this blog post is how you can extend your flows with a bit more advanced behavior by using the Workflow Definition Language. Actually, Microsoft Flow and Azure Logic Apps have a lot in common. Logic Apps allows you to use the Workflow Definition Language, containing a bunch of functions to give you logical behavior, functionality, parsing, conversion, … . The fact is that you can also use that Workflow Definition Language in Microsoft Flow, but it is not documented that well. Ohw, before I forget, the Workflow Definition Language from Logic Apps is described right here. Make sure you dig into that documentation to understand the functions you have at your disposition.

To illustrate how one can use it, I created a flow that will, starting from the PowerApp blog RSS create an email containing the new blog posts. Sounds like a simple one, but you need the Workflow Definition Language in order to make it a bit more fancy…

Starting with the trigger

I’ve decided to go for a trigger that runs every day, so that I can fetch the most recent blog posts every day. The setup looks as follows, still quite simple:

image_thumb1

Filtering out only the “new” ones

I’m only interested in the “new” posts. Since my flow runs on a daily basis, I’m only interested in the posts that are less than a day old. Now the RSS feed items contain a field that contains the Published Date, so I want to filter on this field. In Flow, you can use the action Filter array from Data Operations to filter a collection of items. This will give you a “Simple” condition you can configure, but there is no way you can configure there: “Has to be published in the last 24 hours”. So you switch to “Advanced”, and now it’s up to the Workflow Definition Language to kick in.

image_thumb4

The formula

@greater(item()?['publishDate'], adddays(utcnow(),-1))

checks whether the PublishedDate is greater than right now minus 1 day, giving you the posts created in the last 24 hours.

Preparing the HTML per blog post

Now that I have the most recent blog posts, I can start preparing them. I mean, at the end I want to send an email containing HTML to my email address. So for every item I want to create an <li> item containing an <a> anchor tag. So I want to do an Apply to each and then Concatenate some fields and text together. You can use the Compose action from the Data Operations for this.

image_thumb6

Make sure the formula is SURROUNDED WITH DOUBLE QUOTES or it will not work… The UI trims off the double quotes after save.

"@concat('<li><a href=\"', first(item()?['links']), '\">', item()?['title'], ' </a>')"

Only send an email if any blog post was found

This step could actually be put after the List RSS Items, but I’ve put it after the Apply to each. You can use the following:

image_thumb8

Creating the email body

In the previous steps we already created the HTML for every item in the feed, now we just need to merge those items and surround it with some other HTML, just to make it a bit prettier. We can achieve this with another Compose action.

image_thumb10

Again, make sure this formula is surrounded with double quotes or it will not work.

"@concat('<h1>New posts on the PowerApps blog</h1><ul>', join(outputs('Compose'),''), '</ul>')"

Sending the email

What remains is to send the email. Nothing more then passing on the output of the previous step as the email body. Since we constructed HTML as the body, the email can be sent as HTML too.

image_thumb13

Run the flow, and there is the email:

image_thumb15

Watch out with calculated DateTime fields in CSOM

var localDateOnlyValue = ctx.Web.RegionalSettings.TimeZone.UTCToLocalTime((DateTime)item.FieldValues["DateOnly"]);
ctx.ExecuteQuery();

I recently came across a baffling issue when using CSOM to connect to a SharePoint Online list to retrieve values from a Date and Time field and another field calculated based on the first one.
To illustrate the issue, I created a dummy list with 2 colums:

  • DateOnly: Date and Time (show Date Only)
    01
  • CalculatedDateOnly: Calculated with formula [DateOnly] + 1 (Also showing as Date Only)
    02

 

When creating new items in this list, all looks well. The Date Only and calculations are done correctly.

04

The story in CSOM

Now, up to CSOM, when accessing this list with the following code:

var list = ctx.Web.Lists.GetByTitle("TestDateTime");
var item = list.GetItemById(1);
ctx.Load(item);
ctx.ExecuteQuery();

var dateOnlyValue = item.FieldValues["DateOnly"];
var calculatedDataOnlyValue = item.FieldValues["CalculatedDateOnly"];

 

The values in the dateOnlyValue and calculatedDataOnlyValue are not what you might expect:

05

Observe that the DateOnlyValue is –2 hours from what was originally selected. Now this is perfectly explainable. SharePoint stores the DateTime values in UTC, and when using the web interface, SharePoint takes into account the regional settings of your site to convert between local and UTC. If you use CSOM however, you get the value in UTC, no problem here. However, my calculated field, that just did a +1 (add a day) is not returned in UTC, which is confusing, because i just said that CSOM would return datetime in UTC.

Assuming that the dateOnlyValue is UTC, you can convert this to local time as follows (the inverse also exists):

var localDateOnlyValue = ctx.Web.RegionalSettings.TimeZone.UTCToLocalTime((DateTime)item.FieldValues["DateOnly"]);
ctx.ExecuteQuery();


Now, imagine you need to use these field values to forward to the user or some business process, then you might need logic to convert UTC values to local (for the dateOnlyValue) and other logic that does nothing for the calculated field (calculatedDateOnly). So basicaly, you have to assume that DateTime fields will be UTC and calculated ones will not be UTC. Let’s make that assumption …

The story in REST

Remember the assumption from CSOM: DateTime fields will be UTC and calculated ones will not be UTC.

Let’s see if this assumption stays valid when making REST calls. The following REST call was used to retrieve the values:

/_api/lists/getbytitle('TestDateTime')/Items(1)?$select=DateOnly,CalculatedDateOnly

and the result shows the following (baffling result)

<content type="application/xml">
	<m:properties>
		<d:DateOnly m:type="Edm.DateTime">2017-04-24T22:00:00Z</d:DateOnly> 
		<d:CalculatedDateOnly>2017-04-25T22:00:00Z</d:CalculatedDateOnly> 
	</m:properties>
</content>

Now both are in UTC!!!!!

So I will have to change my assumption to: DateTime fields will be UTC, but in CSOM calculated DateTime fields are local.

Solution

Now what about a solution that works in CSOM? If your goal is to use the values from SharePoint as they are “shown” to the users. Then you’re better off using the property FieldValuesAsText on a listitem.

var list = ctx.Web.Lists.GetByTitle("TestDateTime");
var item = list.GetItemById(1);
ctx.Load(item, it => it.FieldValuesAsText);
ctx.ExecuteQuery();

var dateOnlyValue = item.FieldValuesAsText["DateOnly"];
var calculatedDataOnlyValue = item.FieldValuesAsText["CalculatedDateOnly"];

 

Then both variables will contain the date values as shown in the web interface:

07

Applying JSLink on a Dashboard page

In this article I will discuss the issues I came across with when trying to apply jslink on a SharePoint dashboard page. For me, a dashboard page is a page on which you put multiple web parts, in order to get a global overview on a certain entity.

In order to demonstrate this, consider that you keep track of a normal task list and an issue tracking list, which you relate to the task list by means of a lookup field. I wanted to play around with the percentage complete column, so I made sure that it was added to the default view of the tasks list.
Once this was all set up, I created a new page, with a two column layout on which I add the tasks list and the issues list web parts, and connected them based on the lookup field that I created.

image

image

With the dashboard page created, I could now add some jslink to the tasks web part. I decided to simply style the Due Date and the % Complete. Here is the code I used for this:

var U2U = U2U || {};

U2U.RegisterTemplateOverrides = function () {
    // Fields template registration
    var overrideCtx = {
        Templates: {
            Fields: {
				'DueDate':
                    {
                        'View': U2U.RenderDueDate
                    },
                'PercentComplete':
                    {
                        'View': U2U.RenderPercentCompleteView
                    }
            }
        }
    };
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
};

U2U.RenderDueDate = function (ctx) {
    var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];

    var date = new Date(value);
    var today = new Date();

    return (date < today) ? '<b>' + value + '</b>' : value;
};

U2U.RenderPercentCompleteView = function (ctx) {
    var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];

    // .ms-TopBarBackground-bgColor
    // .ms-EmphasisBackground-bgColor

    var output = [];
    output.push('<div style="display:block; height: 20px; width: 100px;" class="ms-TopBarBackground-bgColor">');
    output.push('<span style="color: #fff; position:absolute; text-align: center; width: 100px;">');
    output.push(value);
    output.push('</span>');
    output.push('<div style="height: 100%; width: ');
    output.push(value.replace(" %", ""));
    output.push('%;" class="ms-EmphasisBackground-bgColor"></div></div>');
    return output.join('');
};

U2U.RegisterTemplateOverrides();

Notice in the code that I’m (ab)using the classes ms-TopBarBackgound-bgColor and ms-EmphasisBackground-bgColor. By using these, even when the composed look of the site would be changed, the task progress bar would still have the same look and feel as the page being hosted on.

After associating this JavaScript file with the tasks web part on my dashboard page, I noticed that the task list check boxes and the stripe through were not working any more. Moreover, the due date in the issue list was also being styled, while I didn’t associate my JavaScript file with that web part.
Now, what’s happening here?

clip_image002[4]clip_image004[4]

Fixing the original rendering of the tasklist

Now for what is happening with the task list, if you dig around in the sources of the web page (make sure the debug.js files are loaded ;-)) and compare your page without jslink to your page with jslink, you’ll find that the file hierarchytasklist.js is the one responsible for styling the task list. In the page that is using the jslink, this file is apparently loaded “long” after your file is loaded. You can find this out by putting some breakpoint in your code and in the hierarchytasklist.js.

clip_image002[6]

In order to solve this, you have to make sure the hierarchytasklist.js is registered before your code starts doing the rendering. You can achieve this by putting the next two lines before calling RegisterTemplateOverrides(ctx). I’m using the debug, because I’m not done with my issues yet …

RegisterSod('hierarchytaskslist.js', '/_layouts/15/hierarchytaskslist.debug.js');
LoadSodByKey('hierarchytaskslist.js', null);

Elio Struyf has described a solution for this in his blog post.

Now after applying this change, and having a look at the final result, the title now looks good, but I lost my rendering on the DueDate.

clip_image002[8]

Overriding the hierarchytasklist templates

To understand what is happening now, you need to understand what the file hierarchytasklist.js is doing. That file itself is actually doing the jslink registrations for a couple of fields in a task list, and thus overwriting your template for DueDate. For example, this is the template registration for DueDate:

function _registerDuedateFieldTemplate() {
ULSEhz:
    ;
    var DuedateFieldContext = {
        Templates: {
            Fields: {
                'DueDate': {
                    'View': window.DuedateFieldTemplate.RenderDuedateField
                }
            },
            ListTemplateType: 171
        }
    };

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(DuedateFieldContext);
}
ExecuteOrDelayUntilScriptLoaded(_registerDuedateFieldTemplate, 'clienttemplates.js');

You’ll notice the use of ExecuteOrDelayUntilScriptLoaded to make sure the registration does not happen until the clienttemplate.js is loaded. The clienttemplate file is the so called “engine” behind jslink doing all the necessary things on your page. Now what we want to make sure is that we register our template after hierarchytasklist has done this, and before the engine starts rendering. So let’s just use the same trick as the hierarchytasklist file is using. I pull the two lines for registration of the hierarchytasklist.js out of my RegisterTemplateOverrides and at the end of the file I add the following line:

ExecuteOrDelayUntilScriptLoaded(U2U.RegisterTemplateOverrides, 'hierarchytaskslist.js');

After applying this, problem solved! Using some breakpoints, you’ll notice that the original template is registered first but then overridden by yours.

clip_image002[10]

Back to the dashboard

Now, I didn’t look at my dashboard page for a while, assuming everything would be just fine. However, I went to the dashboard page and noticed my rendering wasn’t working at all, nor the original task hierarchy templates.

The main reason for this was that my dashboard page was running on a site with Minimal Download Strategy (MDS) enabled. So I fixed that as described here and replaced my last line, which waited on hierarchytasklist.js to be loaded, by:

//Register for MDS enabled site otherwise the display template doesn't work on refresh
U2U.sitePath = window.location.pathname.substring(0, window.location.pathname.indexOf("/_layouts/15/start.aspx"));
// CSR-override for MDS enabled site
RegisterModuleInit(U2U.sitePath + "/SiteAssets/JSLINK/tasks.js", U2U.RegisterTemplateOverrides);
//CSR-override for MDS disabled site
U2U.RegisterTemplateOverrides();

But of course, since I’m not waiting on the task hierarchy templates to be loaded any more, I lose the original styling again (i.e. check boxes, stripe-through title, …). I need to make sure that I wait for the task hierarchy templates to be loaded before clienttemplate.js can start doing the rendering.

The problem however is that you can wait for hierarchytasklist.js to be loaded, but clienttemplates.js doesn’t wait for it. If you haven’t registered your templates before he starts rendering them, then they are not applied at all. So the trick is to make sure that before he renders, everything is loaded. If you have a look at the source of your page and the file clienttemplate.debug.js, you’ll find a function RenderListView that is called from within your page, and actually starts the rendering.

clip_image002[12]

So why not make sure, that from the moment the clienttemplates.js is loaded, we alter the RenderListView function so that it waits for the necessary things to be loaded, in our case the task list hierarchy templates. So I removed the code for the MDS registration and replaced as follows, fixing the dashboard!

ExecuteOrDelayUntilScriptLoaded(
	function(){
		//Register for MDS enabled site otherwise the display template doesn't work on refresh
		U2U.sitePath = window.location.pathname.substring(0, window.location.pathname.indexOf("/_layouts/15/start.aspx"));
		// CSR-override for MDS enabled site
		RegisterModuleInit(U2U.sitePath + "/SiteAssets/JSLINK/tasks.js", U2U.RegisterTemplateOverrides);
		//CSR-override for MDS disabled site
		U2U.RegisterTemplateOverrides(); 
	},
	'hierarchytaskslist.js');

// Now override the RenderListView once the ClientTemplates.JS has been called
ExecuteOrDelayUntilScriptLoaded(
    function () {
        //Take a copy of the existing definition of RenderListView
        var originalRenderListView = RenderListView;

        //Now redefine RenderListView with our override
        RenderListView = function (ctx, webPartID) {
			ExecuteOrDelayUntilScriptLoaded(
				function(){
					// Call the original RenderListView
					originalRenderListView(ctx, webPartID);
				},
				'hierarchytaskslist.js');
        };
    }, 'clienttemplates.js');

 

clip_image002[14]

What about that DueDate column in the issues list?

We still see the DueDate column in the issues list being styled too, which is weird. That is actually because the clienttemplate.js engine applies your templates on the whole page and if your template does not limit to a certain list, view, or other, it will match any column with the same name. Now, when registering your template, you can set the property ListTemplateType to 171 to make sure it only works on task lists.
The property ListTemplateType will not help if you have multiple task list view web parts on your page that need different rendering for the same columns, have a look here on how you could possibly overcome this.

If you want to review the complete file, you can find it on GitHub.

What’s new in SharePoint 2016 CSOM (DocumentSets)

With SharePoint 2016 RC available, it’s interesting to see what new additions we have in CSOM and/or REST APIs. Doing some research in the differences between the SharePoint 2013 RTM CSOM and SharePoint 2016 RC CSOM, I noticed several differences. This post will describe the differences related to DocumentSets.

In SharePoint 2013, the only thing you could do with document sets from within CSOM was to create one, given a parent folder, name and the contenttype id of the document set. You weren’t able to do anything else.

So what’s new? You’re now also able to do the following things:

  • Create/alter the contenttype definition of a documentset contenttype.
  • Get a document set based on a listitem/folder.
  • Export a document set to a zip file (I mean OpenXML file).
  • Import a document set from a zip file (again … OpenXML file).

image

DocumentSetTemplate

There is a new class available that allows you to change the definition of a documentset contenttype. It gives you access to the SharedFields, Templates, AllowedContentTypes, …
You can use it as follows:

var documentSetTemplate = DocumentSetTemplate.GetDocumentSetTemplate(ctx, ct);
documentSetTemplate.AllowedContentTypes.Add(ct_document.Id);
documentSetTemplate.DefaultDocuments.Add("template.odt", ct_document.Id, bytes);
documentSetTemplate.SharedFields.Add(field_customer);
documentSetTemplate.WelcomePageFields.Add(field_customer);
documentSetTemplate.Update(true);
ctx.ExecuteQuery();

Where ct and ct_document are respectively a documentset contenttype and a document contenttype.

Unfortunately, you’re still not able to directly set the default document set view, nor alter the url of the docsethomepage.aspx.

Export Document Set

You can now export a documentset to an OpenXML (zip) file as follows:

ListItem item = GetDocumentSet(...); // DocumentSet item
ctx.Load(item, it => it.Folder);
ctx.ExecuteQuery();

var documentSet = DocumentSet.GetDocumentSet(ctx, item.Folder);
ctx.Load(documentSet);
ctx.ExecuteQuery();

var stream = documentSet.ExportDocumentSet();
ctx.ExecuteQuery();
using(FileStream fs = new FileStream("docset.zip", FileMode.Create, FileAccess.Write, FileShare.None))
{
    stream.Value.CopyTo(fs);
}

Result:

image

Import Document Set

You can now import an exported document set as follows:

using(FileStream fs = new FileStream("docset.zip", FileMode.Open, FileAccess.Read, FileShare.None))
{
    DocumentSet docSet = DocumentSet.ImportDocumentSet(ctx, fs, "Imported", list.RootFolder, ct.Id);
    ctx.ExecuteQuery();
}

Result:

image

Announcing the SharePoint Add-in “Export to Word”

Today, we’re glad to announce the FREE SharePoint Add-in “Export to Word”, developed by the team at U2U. This add-in fixes one of those issues that you’ll probably will have come across with in SharePoint, namely generating Word documents based on SharePoint list items. A bit like a mail merge for list items!

This functionality has never been out of the box available for regular SharePoint list items. You could achieve something like it on document libraries only, using Quick Parts in Word. But however, that only works for document libraries.

Now, what does the add-in allow you to do? It allows you to configure, per SharePoint list, one or more Word templates. You can even upload your own templates! The add-in then allows you to link fields from your selected list to the content of the template. Once your template is completely designed, you can just go to the SharePoint list and export any item to Word using your template. You can even export multiple items in bulk!

CreateNewTemplate_ori GeneratedDocuments_ori GeneratedDocuments2_ori

Do you want to know more about this add-in? Just go to the official site.
Do you want to install it from the Office Store? You can find the add-in here.

For a quick start guide on how to work with the add-in, have a look at the following video:

 

Do note that this is the first version of the Add-in, and we want your feedback to further extend and improve the add-in. You can contact us via the regular ways.

An Office 365 App for Site Collection Provisioning in SharePoint Online

Continuing on my previous post, I decided to create a new Office 365 app that would be able to manage site collections in SharePoint Online.

When deciding to build an app that does site collection provisioning, you could chose to do this as a SharePoint App, but SharePoint Apps are typically contextual to the web they're hosted in. With the introduction of Office 365 Apps, which are contextual to the Office 365 tenant, provisioning site collections from an app seems more to be a task for an Office 365 App.

My new application should be capable of:

  • Getting an overview of available site collections
  • Creating a new site collection
  • Delete an existing site collection

My previous post proved that you can access the SharePoint Online sites by means of the Client Side Object Model (CSOM). My goal now is to prove that you can also do SharePoint online administration tasks from an Office 365 app, using also CSOM.

If you’re interested in the full source code, you can get it from GitHub. There you’ll also find how you can register the app in Azure Active Directory.

Accessing SharePoint Online Administration

As an extension to the SharePoint Client Side Object Model, you have the “Microsoft.Online.SharePoint.Client.Tenant.dll” library which gives you access to SharePoint Online Administration tasks. I.e. creating/deleting site collections, managing deleted site collections, … .

Now, would it then be possible to use the Azure application registration and authentication to i.e. manage site collections? Yes, it is possible!
In my previous post, I mentioned how you obtain the access token from Azure Active Directory. You can then get an accessToken for SharePoint Online administration as follows:

string clientID = ConfigurationManager.AppSettings["ida:ClientId"] ?? ConfigurationManager.AppSettings["ida:ClientID"];
string appKey = ConfigurationManager.AppSettings["ida:AppKey"] ?? ConfigurationManager.AppSettings["ida:Password"];
string spSiteUrl = "https://tenant-admin.sharepoint.com";

string accessToken = await GetAccessToken(clientID, appKey, spSiteUrl)

Take care that you replace tenant by your tenant name. Now that you have the accesToken, you can create the ClientContext as follows:

ClientContext clientContext = new ClientContext("https://tenant-admin.sharepoint.com");
clientContext.ExecutingWebRequest +=
    (sender, e) =>
    {
        e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + accessToken;
    };

Getting an overview of all the site collections

Now that you have the ClientContext, you can start using the Tenant class to get an overview of the available site collections. You can do this as follows:

// Create tenant
Tenant tenant = new Tenant(ctx);

// Get the site properties
SPOSitePropertiesEnumerable allSiteProperties = tenant.GetSiteProperties(0, true);

// Load
ctx.Load(allSiteProperties);
await ctx.ExecuteQueryAsync();

The GetSiteProperties method currently only returns a maximum of 300 site collections. If you would like to get the next 300, increment the startIndex parameter to 300.

Creating a new site collection

In order to create a new site collection, you need to know the code of the webtemplate that should be applied to the new site collection. Now if you know the template code by hart, you can just pass it as a string, but better is to first check the available templates in SharePoint Online and chose the one you want to use. Because you have a ClientContext to your SharePoint Online Administration, you can check the web property for available web templates. You can do this as follows:

// Get the webtemplates
WebTemplateCollection webTemplates = ctx.Web.GetAvailableWebTemplates(lcid, false);

// Load
ctx.Load(webTemplates, templates => templates.Where(t => t.IsHidden == false));

// Execute
await ctx.ExecuteQueryAsync();

The GetAvailableWebTemplates method takes in the language code lcid. Here also, if you know the language code that is available by hart, you can just pass it in here. Another approach would be to first check which languages are available in SharePoint Online:

// Load
ctx.Load(ctx.Web, w => w.SupportedUILanguageIds);

// Execute query
await ctx.ExecuteQueryAsync();

Now that we have the ClientContext, Language and template; you can create a new site collection. The Tenant class provides a method called CreateSite that accepts a SiteCreationProperties object as input and creates a new site collection using the properties supplied in the SiteCreationProperties instance.

The snippet below illustrates how you can create a new site collection. In this snippet, you might observe that the CreateSite method returns an SpoOperation. Now, any operation that we can do on a site collection will return an SpoOperation. So when we create a new site collection, it will return an SpoOperation containing the information about the site collection creation operation. You can check the IsComplete property on the SpoOperation to check whether the operation has completed or not (reload the SpoOperation to check again), this is because the creation of a site collection happens asynchronously.

// Create tenant
Tenant tenant = new Tenant(ctx);

// Create properties
SiteCreationProperties siteCreationProperties =
    new SiteCreationProperties()
    {
        Url = "https://tenant.sharepoint.com/sites/createdbycode"
        ,
        Title = "Site created by code"
        ,
        Owner = "admin@tenant.onmicrosoft.com"
        ,
        Template = "STS#0"
        ,
        StorageMaximumLevel = 100
        ,
        UserCodeMaximumLevel = 50
    };

// Create the sitecollection
SpoOperation operation = tenant.CreateSite(siteCreationProperties);

// Execute query
ctx.ExecuteQuery();

Deleting an existing site collection

In order to delete an existing site collection, the basic steps are again the same. The difference is the method that will be called from the Tenant class, being RemoveSite. Do note that the site collection is not permanently deleted, but is pushed to the recycle bin in which it can still be restored. Note that the RemoveSite also returns an SpoOperation.

// Create tenant
Tenant tenant = new Tenant(ctx);

// Perform delete
SpoOperation spoOperation = tenant.RemoveSite("https://tenant.sharepoint.com/sites/createdbycode");

// Load and execute
ctx.Load(spoOperation);
await ctx.ExecuteQueryAsync();

App screenshots

Overview of sites:

05

Creating a new site:

06

Consuming SharePoint CSOM from an Office 365 app

I’ve been a C# developer for more than five years now. When SharePoint 2013 was released, I started doing development for SharePoint 2013 and later also SharePoint Online. My focus was on developing web services (in combination with the SharePoint App model), native client and mobile applications using the Client Side Object Model.

When I was at the Barcelona TechEd conference in 2014, I decided to follow quite some breakout sessions about Office 365 and the “new” Office 365 APIs. It was around that time also that they released the new Office 365 Apps look and feel by means of the new “App Launcher” and the “My Apps” page.

clip_image002

Without having the knowledge about Apps for Office 365, I initially thought that these would be quite comparable to Apps for SharePoint. However, Apps for Office 365 are completely different.

Apps for Office 365

An app for Office 365 is conceptually:

  • An application that is running externally (i.e. on some website) or standalone (i.e. mobile or desktop). The application can be accessible from within the office 365 website, but also could be surfaced from within Word or Outlook.
  • Integrating somehow with Office 365 (this is not required).
  • Registered in the Azure Active Directory (AAD) that is running behind your Office 365 tenant.
  • Can use the same authentication mechanism as Office 365, resulting in single sign-on.

So before you can use your app in Office 365, you have to register it in Azure Active Directory. When registering you app in Azure AD, you also configure the permissions it gets to access the Office 365 services like mail, contacts, events and OneDrive for Business, but also SharePoint! Then I started thinking: “Does this mean that you can register an app in Azure AD and let it access SharePoint online?”.

clip_image004

Getting the access token

I was eager to discover whether it was possible to access SharePoint using the Client Side Object Model and the Azure Authentication mechanism. Jeremy Thake briefly mentioned in a blog post that it is possible to use the token you get from Azure as the Bearer token in you ClientContext requests.
So I created an ASP.NET MVC5 web application, registered it in Azure AD, set the permissions for Office 365 SharePoint Online (see above) and started experimenting.

You can get the access token as follows:

/// <summary>
/// Get the access token for the required resource
/// </summary>
/// <param name="clientID">The client ID of your app</param>
/// <param name="appKey">The app key</param>
/// <param name="resource">The resource you would like access to</param>
/// <returns></returns>
public static async Task<string> GetAccessToken(String clientID, String appKey, String resource)
{
    // Redeem the authorization code from the response for an access token and refresh token.
    ClaimsPrincipal principal = ClaimsPrincipal.Current;

    String authorizationUri = "https://login.windows.net";
    String nameIdentifier = principal.FindFirst(ClaimTypes.NameIdentifier).Value;
    String tenantId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

    AuthenticationContext authContext = new AuthenticationContext(
        string.Format("{0}/{1}", authorizationUri, tenantId),
        new ADALTokenCache(nameIdentifier)
    );

    try
    {
        // Get the object identifier
        String objectId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

        // Authenticate
        AuthenticationResult result = await authContext.AcquireTokenSilentAsync(
            resource,
            new ClientCredential(clientID, appKey),
            new UserIdentifier(objectId, UserIdentifierType.UniqueId)
        );

        return result.AccessToken;
    }
    catch (AdalException exception)
    {
        //handle token acquisition failure
        if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
        {
            authContext.TokenCache.Clear();
        }
    }

    return null;
}

You obtain the values for the ClientID and AppKey from the application page in Azure AD. You typically store the values for these properties in the configuration file of your application.

clip_image006

Now what is the Resource you have to supply? Well the resource is as follows:

So in order to access a SharePoint, you can get the access token as follows:

String clientID = ConfigurationManager.AppSettings["ida:ClientId"] ?? ConfigurationManager.AppSettings["ida:ClientID"];
String appKey = ConfigurationManager.AppSettings["ida:AppKey"] ?? ConfigurationManager.AppSettings["ida:Password"];
String spSiteUrl = "https://tenant.sharepoint.com";

String accessToken = await GetAccessToken(clientID, appKey, spSiteUrl)

Accessing a SharePoint Online Site

In order to be able to use this token for your ClientContext, you need to set the Authorization Header on each webrequest the clientcontext sends out:

ClientContext clientContext = new ClientContext(spSiteUrl);
clientContext.ExecutingWebRequest +=
    (sender, e) =>
    {
        e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + accessToken;
    };

You can now access this site through the clientcontext you created (taking into account the permissions you’ve set in Azure AD). In case you do not have the permissions to access something through the clientcontext, you’ll typically get a UnauthorizedaccessException.

Research Tracker application

Jeremy Thake often refers to a cool Office 365 project that combines different application types to access the same data in your SharePoint site. You can find these projects on GitHub.
One of these projects uses the SharePoint REST API to access a SharePoint site, create lists, manage list items. I cloned this project and extended it to also include the functionality by means of CSOM. You can also find this project on GitHub.

TextBox