Creating Office 365 applications (The Complete Guide 2019)

Creating Office 365 applications

How to Create Office 365 applications

Throughout this blog, you will see how to create a solution that leverages some of the most interesting capabilities of Office 365 applications and the Microsoft Graph. The goal is to create a custom ASP.NET MVC solution that extends Office 365 to create custom digital workplaces to manage business projects.

 

The resulting application will be accessible through a tile in the app launcher and by extending the out-of-box UI of SharePoint Online with some custom actions. The sample solution provides some collaboration-oriented capabilities, enabling you to start a new business project with a focus group of people working on it. Under the cover of every project, there will be an Office 365 Group that will hold the documents, a shared calendar, and the communications related to the project.

 

The custom application will provide some customized UI elements to interact with each project and to monitor the overall process by sending automated email messages and feeding the Office 365 Group custom notifications and events by leveraging the Office 365 Connectors.

 

Overall, the solution will give you some guidance about how to accomplish the following tasks:

  • Creating an Office 365 application by using Microsoft Visual Studio 2015
  • Configuring the application to act on behalf of the current user via OAuth 2.0 or with an app-only access token
  • Leveraging the Office UI Fabric to provide a common and well-known user experience to the application users
  • Using the Microsoft Graph to interact with Office 365 Groups and Microsoft Exchange Online
  • Using the Microsoft SharePoint REST API to consume SharePoint Online
  • Leveraging controls and libraries provided by the Office 365 Developer Patterns & Practices (PnP) community project
  • Leveraging an asynchronous development pattern and a Microsoft Azure WebJob to create more scalable and reliable solutions
  • Creating an Office 365 Connector to asynchronously interact with an Office 365 Group

 

Most of the topics covered in this blog will be useful for your everyday work when you are developing custom solutions for Office 365.

 

Creating and registering the Office 365 application

The development platform for the sample application will be Microsoft Visual Studio 2015 (Update 2). First, you have to create a new empty solution, which in the samples related to this blog is called BusinessApps.O365ProjectsApp, together with a new project of type ASP.NET web application, which in the current sample is called BusinessApps.O365ProjectsApp.WebApp.

 

Note

You can download the full sample solution from GitHub at the following URL: https://github.com/OfficeDev/PnP/tree/master/Samples/BusinessApps.O365ProjectsApp.

Within the same GitHub repository, you will find other useful samples and solutions, ready to be used for creating your own solutions. You can select to create a new ASP.NET 4.5.x MVC web application, which will provide both MVC and Web API capabilities. To target Office 365, you have to select the Work And School Accounts authentication model, providing the target tenant name.

 

You should also select to have read access to directory data so that Visual Studio will register a new Client Secret for your application, from an Open Authorization perspective. This will also enrich the Startup.Auth.cs file with statements to handle not only the OpenID Connect authentication, but also the OAuth 2.0 authorization.

 

Try to start your application by pressing F5 in Visual Studio, and you will be prompted for authentication against the Microsoft Azure Active Directory (Azure AD) tenant that is under the cover of your target Office 365 tenant. Just after the authentication phase, you will see the home page of the The ASP.NET MVC site.

 

So far, starting the application will give you quite a bitter feeling because the UI will be the one available out of the box for any ASP.NET  MVC application, which is unlike the user interface and experience of Office 365.  Nevertheless, even with the out-of-box ASP.NET MVC UI, you will see the currently logged-in user name in the upper-right corner of the screen.

 

Azure AD application general registration

So far, you have created and registered the application in Azure AD. However, to set up the application properly, you probably will also need to configure a custom logo for it. The custom logo will be used in the Office 365 app launcher to show your application. To configure a custom logo, go to the Azure AD tenant under the cover of your Office 365 tenant, open the Applications tab, search for your custom application (by using either the Client ID or the application name), and open the application Configuration tab.

 

At the beginning of the page, you will find the default application logo, and in the lower part of the screen you will have an Upload Logo button. Click that button and choose a custom logo image, which has to adhere to the following requirements:

 

Image dimensions of 215 × 215 pixels

Central image dimensions of 94 × 94 pixels, with the remainder of the image as a solid field

Supported file types: BMP, JPEG, and PNG

File size less than 100 KB

 

In the current sample, we will use the OfficeDev PnP logo because this sample application will be hosted under the PnP family of samples. After registering the application logo, you can open the browser and go to your Office 365 tenant account. Click the app launcher and select the View All My Apps link in the lower-left corner of the app launcher.

 

There, you will find the full list of native and custom Office 365 applications available to your user, including the just-configured application. If you want to pin the new application to the app launcher, you can click the ellipses in the upper-right corner of the app logo and select the Pin To App Launcher menu item. 

 

If later you would like to remove the application from the app launcher, just come back to the View All My Apps page, select the app, click the ellipses, and select the Unpin From App Launcher menu item.

 

Note

At the time of this writing, is not possible to automate the process of pinning an application to the app launcher. In the future, Microsoft may make an API to automate the process, but this is not guaranteed.

 

App-only authorization registration

The sample application that is built throughout this blog requires you to accomplish some tasks acting as “app-only” from an authorization perspective. Thus, in this section you will learn how to configure the application in Azure AD to be able to interact with SharePoint Online and the Microsoft Graph with an app-only token.

 

First of all, you will need to create a self-signed X.509 certificate that will be used to authenticate your application against Azure AD while providing the app-only access token. The certificate can be created using the makecert.exe command-line tool, which is included in the Windows SDK. In the following excerpt, you can see the syntax to invoke the makecert command:

makecert -r -pe -n "CN=ApplicationName" -b 05/01/2016 -e 05/01/2018 -ss my -len 2048

 

The statement instructs the tool to create a self-signed certificate (option -r), with an exportable private key (option -pe), with a common name value of “CN=ApplicationName” (option -n), valid from May 1 2016 (-b) to May 1 2018 (-e). Moreover, the certificate will be stored in the personal certificates secure store of the current user (-ss my), and the generated key length will be 2,048 bits (-len 2048).

 

Another option that you have is to leverage the PowerShell cmdlet called New-SelfSignedCertificate, which is newer and more powerful. This option is my favorite because you can easily create a PowerShell script that automates the configuration process. However, if you don’t like PowerShell, or if you are not familiar with it, you can fall back to the makecert option.

 

Note

You can find further details about the makecert tool at the following URL: https://msdn.microsoft.com/en-us/library/bfsktky3(v=vs.100).aspx. You can find more information about the New-SelfSignedCertificate cmdlet at the following URL: https://technet.microsoft.com/en-us/library/hh848633.

 

There is a sample solution called PnP Partner Pack that is available in the OfficeDev PnP offering at the following URL: http://aka.ms/OfficeDevPnPPartnerPack. That sample solution uses the app-only authorization, like the sample application about which you are learning in this blog.

 

One key benefit of having a look at the PnP Partner Pack is that the setup guide of the project provides you with a PowerShell script (https://github.com/OfficeDev/PnP-Partner-Pack/blob/master/scripts/Create-SelfSignedCertificate.ps1) that gives you a ready-to-go solution for creating a self-signed certificate, which will be automatically installed in the certificate store and also saved in the local file system.

 

More info

The PnP Partner Pack is a sample solution provided to the community as an open source project in GitHub. The goal of the PnP Partner Pack is to show how to leverage the patterns, guidance, and tools that PnP provides through a real solution that can be considered a startup project for real business use cases.

 

The main capabilities of the PnP Partner Pack are: it is an Office 365 application; it provides the capability to do self-service site and site collection creation based on PnP provisioning templates; it allows you to save a site as a template (PnP provisioning template) directly from the web UI of SharePoint; and it provides sample jobs for governance purposes. The PnP Partner Pack is available at the following URL: http://aka.ms/OfficeDevPnPPartnerPack.

 

Once you have created the certificate, you can browse to the Configuration page of the Office 365 application in the Azure AD management portal and click the Manage Manifest button, which is available in the lower part of the screen. Select the Download Manifest option, and the browser will start to download a .JSON file that represents the manifest of the app. 

 

LISTING The .JSON manifest file of an Office 365 application registered in Azure AD

{
"appId": "74c393a9-b865-48b7-b2b6-0efa7a2305a1",
"appRoles": [],
"availableToOtherTenants": false,
"displayName": "BusinessApps.O365ProjectsApp.WebApp",
"errorUrl": null,
"groupMembershipClaims": null,
"homepage": "https://localhost:44304/",
"identifierUris": [
"https://tenant.onmicrosoft.com/BusinessApps.O365ProjectsApp.WebApp"
],
"keyCredentials": [],
"knownClientApplications": [],
"logoutUrl": null,
"oauth2AllowImplicitFlow": false,
"oauth2AllowUrlPathMatching": false,
"oauth2Permissions": [
{
"adminConsentDescription": "Allow the application to access BusinessApps. O365ProjectsApp.WebApp on behalf of the signed-in user.",
"adminConsentDisplayName": "Access BusinessApps.O365ProjectsApp.WebApp",
"id": "11851dd6-95ef-4336-afca-8e8d9212d5ec",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow the application to access BusinessApps. O365ProjectsApp.WebApp on your behalf.",
"userConsentDisplayName": "Access BusinessApps.O365ProjectsApp.WebApp",
"value": "user_impersonation"
}
],
"oauth2RequirePostResponse": false,
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2017-04-25T16:18:21.3676329Z",
"keyId": "1892ef1f-f2c3-448c-89df-d3b77cf0628c",
"startDate": "2016-04-25T16:18:21.3676329Z",
"value": null
},
{
"customKeyIdentifier": null,
"endDate": "2017-04-25T15:56:46.1588957Z",
"keyId": "8ff689f8-724a-417f-b754-a11ef88eff88",
"startDate": "2016-04-25T15:56:46.1588957Z",
"value": null
}
],
"publicClient": false,
"replyUrls": [
"https://localhost:44304/"
],
"requiredResourceAccess": [
{
"resourceAppId": "00000002-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
"type": "Scope"
},
{
"id": "5778995a-e1bf-45b8-affa-663a9f3f4d04",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"extensionProperties": [],
"objectType": "Application",
"objectId": "695258ca-fe41-44f8-8a56-2e3560164e7c",
"deletionTimestamp": null,
"createdOnBehalfOf": null,
"createdObjects": [],
"manager": null,
"directReports": [],
"members": [],
"memberOf": [],
"owners": [],
"ownedObjects": []
}

So far, the interesting part for you is the property named keyCredentials, of type array, which is highlighted in bold. There are many other useful settings stored within the manifest file, but they are out of the scope of this book. To configure an X.509 certificate as a credential set for the application, you will have to provide a value for the keyCredentials array.

 

Luckily, by using another PowerShell cmdlet that is available within the set of PowerShell cmdlets of PnP, you will be able to use the following syntax to create the KeyCredentials array from the X.509 certificate that you have just created.

Get-SPOAzureADManifestKeyCredentials -CertPath <path to your .cer file> | clip
This statement will copy onto the clipboard of your machine a JSON excerpt like the following:
"keyCredentials": [
{
"customKeyIdentifier": "<Base64CertHash>",
"keyId": "<KeyId>",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"value": "<Base64Cert>"
}
],

 

The values <Base64CertHash><KeyId>, and <Base64Cert> are just sample placeholders. In reality, they will hold the corresponding values generated from the X.509 certificate that you generated.

 

You just need to paste that JSON excerpt, replacing the empty keyCredentials array. Then, save the updated manifest file and upload it back to Azure by using the Upload Manifest option, which is available under the Manage Manifest menu item. Later in this blog, you will learn how to use the certificate to access resources with an app-only access token.

 

Setting Azure AD permissions

Setting up credentials of Office 365 applications enables you to leverage the authorization rules through Azure AD. Thus, it is now time to configure proper permissions for the application both when acting on behalf of the current user and when acting as app-only. You are now ready to implement the solutions, leveraging the services and capabilities provided by Azure AD and the Microsoft Graph.

 

Basic UI elements with Office UI Fabric

A professional and real business-level Office 365 application has to provide the users a UI/UX that makes them feel like they are using Office 365 and not an external solution. Most users already know how to interact with Office 365 and its core services by leveraging a set of well-known controls and icons.

Since late 2015, Microsoft has provided an open source project called Office UI Fabric which provides a rich set of tools, markup, and styles to mimic the UI/UX of Office 365 in any custom software solution. You can find the Office UI Fabric entry point at the following URL:

http://dev.office.com/fabric.

 

From a developer perspective, you can reference Office UI Fabric in many different ways, which are documented at the following URL: http://dev.office.com/fabric/get-started. In the current sample project, the easiest way to use Office UI Fabric is to reference its corresponding NuGet package, which is named OfficeUIFabric. Here, you can see the short command to install the package using the NuGet Package Manager Console: PM> Install-Package OfficeUIFabric

 

The NuGet package will install all the needed .CSS and .JS files into your project so that you will be ready to benefit from using Office UI Fabric, as you will see in the following paragraphs. Another option you have is to reference those files directly from a content delivery network (CDN). Regardless of how you access the Office UI Fabric files, what matters is what you can do with them.

 

When getting the Office UI Fabric through NuGet, you will have to reference its .CSS and .JS files in the BundleConfig.cs file of the MVC project. Thus, open the BundleConfig.cs file under the App_Start folder and update it according to what is highlighted in bold in Listing.

 

LISTING The updated version of BundleConfig.cs, with added or updated parts highlighted in bold

using System.Web;
using System.Web.Optimization;
namespace BusinessApps.O365ProjectsApp.WebApp { public class BundleConfig {
public static void RegisterBundles(BundleCollection bundles) {
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( "~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));
bundles.Add(new ScriptBundle("~/bundles/fabric").Include( "~/Scripts/jquery.fabric.*"));
bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/bootstrap.css",
"~/Content/Office365SuiteBar.css",
"~/Content/fabric.css",
"~/Content/fabric.components.css",
"~/Content/site.css"));
}
}
}

You need to add the script bundle named ~/bundles/fabric, and you have to add the fabric.css and fabric.components.css files to the default style bundle named ~/Content/css. In Listing, you can also see the Office365SuiteBar.css file, which will be explained in the following section and is not related directly to the Office UI Fabric project.

 

Moreover, you will need to update the shared layout CSHTML file to reference the new Office UI Fabric JavaScript bundle, as you will see in Listing in the next section.

 

Office 365 suite bar and top navigation

The first UI element that you should provide within your application is the Office 365 suite bar, which is the one placed in the upper edge of the screen, with the app launcher, the title of the current application, the current user’s picture and profile, and some other context-related links and menu items.

 

Unfortunately, at the time of this writing there isn’t a ready-to-go component in the Office UI Fabric to provide the Office 365 suite bar to your custom applications. Maybe it will come in the future, but for now you have to build it yourself. Of course, you can try to copy and reuse as much as you can from the real suite bar.

 

Nevertheless, it can be a painful task. In Listing, you can see a sample custom layout template for Microsoft ASP.NET MVC, which reproduces a minimalist version of the UI and the behavior of the Office 365 suite bar, excluding the app launcher and some other functionalities.

 

LISTING The CSHTML code of a _Layout.cshtml file that partially mimics the behavior of the Office 365 suite bar

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<meta name=viewport content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title</title>
<script type=text/javascript class='lazy' data-src="https://ajax.aspnetcdn.com/ajax/4.0/1/ MicrosoftAjax.js"></script>
@Scripts.Render("~/bundles/jquery")
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
@Html.Partial("~/Views/Shared/_Office365SuiteBar.cshtml")
@Html.Partial("~/Views/Shared/_Office365NavBar.cshtml")
<div class=scrollableContent>
<div id=mainContent>
<div class=ms-Grid>
<div class=ms-Grid-row>
<div class="ms-Grid-col ms-u-sm1 ms-u-md1 ms-u-lg2"> <img class='lazy' data-src=/AppIcon.png class=siteIcon />
</div>
<div class="ms-Grid-col ms-u-sm11 ms-u-md11 ms-u-lg10"> <h1>@ViewBag.Title</h1>
</div>
</div>
<div class=ms-Grid-row>
<div class="ms-Grid-col ms-u-sm1 ms-u-md1 ms-u-lg2"> </div>
<div class="ms-Grid-col ms-u-sm11 ms-u-md11 ms-u-lg10"> @RenderBody()
</div>
</div>
<div class=ms-Grid-row>
<div class="ms-Grid-col ms-u-sm12 ms-u-md12 ms-u-lg12"> <hr />
<footer>
(C) Office 365 Developers Patterns &amp; Practices,
2016
</footer>
</div>
</div>
</div>
</div>
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/fabric")
@RenderSection("scripts", required: false)
<script type=text/javascript>Initialize the NavBar object if($.fn.NavBar){$(".ms-NavBar").NavBar();}</script>
</body>
</html>
As you can see, the SuiteBar is included through a couple of custom MVC partial views. The first one (“~/Views/Shared/_Office365SuiteBar.cshtml”) mimics the top suite bar, while the second one (“~/Views/Shared/_Office365NavBar.cshtml”) is a custom top navigation bar that looks like the top navigation bar of OneDrive for Business by leveraging the Office UI Fabric icons and menu styles.

 

Moreover, there are some statements to include the bundled scripts and styles that we discussed in the previous section. However, in Listing you can see an interesting excerpt, in which the rendering of the current user’s photo is managed by leveraging the Microsoft Graph API and a custom MVC controller. Moreover, in Listing you can see the use of the Persona control of Office UI Fabric, which renders a user’s picture and online status by using a well-known layout.

 

LISTING An excerpt of the CSHTML code of a _Office365SuiteBar.cshtml file that partially mimics the behavior of the Office 365 suite bar

@if (System.Security.Claims.ClaimsPrincipal.Current != null && System.Security.Claims.
ClaimsPrincipal.Current.Identity != null && System.Security.Claims.ClaimsPrincipal.
Current.Identity.IsAuthenticated) {
<div role="banner" aria-label="User settings">
<div class="o365cs-nav-topItem o365cs-rsp-tn-hideIfAffordanceOn"> <div class="ms-Persona ms-Persona--s">
<div class="ms-Persona-imageArea">
<div class="ms-Persona-initials ms-Persona-initials-- blue">@(BusinessApps.O365ProjectsApp.WebApp.Components.MSGraphAPIContext. CurrentUserInitials)</div>
<img class="ms-Persona-image" class='lazy' data-src="/Persona/GetPhoto?

upn=@(BusinessApps.O365ProjectsApp.WebApp.Components.MSGraphAPIContext.CurrentUs

4&width=64" title="@(BusinessApps.O365ProjectsApp.WebApp.Components.MSGraphAPIContext.

CurrentUserDisplayName)">
</div>
<div class="ms-Persona--offline"></div> </div>
</div>
</div>
}

 

As you can see, highlighted in bold there is an IMG element that renders a dynamic image, which is rendered through a custom controller named PersonaController. In Listing you can see the full implementation of that PersonaController.

 

LISTING The source code of the PersonaController, which renders the current user’s profile picture by using the Microsoft Graph

using BusinessApps.O365ProjectsApp.WebApp.Components; using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using http://System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BusinessApps.O365ProjectsApp.WebApp.Controllers {
[Authorize]
public class PersonaController : Controller {
public ActionResult GetPhoto(String upn, Int32 width = 0, Int32 height =
0) {
Stream result = null;
String contentType = "image/png";
var sourceStream = GetUserPhoto(upn);
if (sourceStream != null && width != 0 && height != 0) { Image sourceImage = Image.FromStream(sourceStream);
Image resultImage = ScaleImage(sourceImage, width, height);
result = new MemoryStream();
resultImage.Save(result, ImageFormat.Png);
result.Position = 0;
}
else {
result = sourceStream;
}
if (result != null) {
return base.File(result, contentType);
}
else {
return new
HttpStatusCodeResult(system.net - Domain Name For Sale | Undeveloped.HttpStatusCode.NoContent);
}
}
<summary>
This method retrieves the photo of a single user from Azure AD
</summary>
<param name="upn">The UPN of the user</param>
<returns>The user's photo retrieved from Azure AD</returns> private static Stream GetUserPhoto(String upn) {
String contentType = "image/png";
var result = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}users/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn), contentType);
return (result);
}
private Image ScaleImage(Image image, int maxWidth, int maxHeight) { var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height; var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio); var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage)) graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
}
}

 

The key implementation is in the GetUserPhoto method, which uses the Microsoft Graph API according to retrieve the binary content of the user’s profile picture. There are also some plumbing functions to resize and convert the image into the proper output format for the target browser.

 

You can see the content of the _Office365NavBar.cshtml partial view, which provides the UI for the top navigation bar. For the sake of completeness, the sample navigation bar leverages some of the most useful primitives of Office UI Fabric for creating menus, just to show you how to use them.

 

LISTING The CSHTML code of the _Office365NavBar.cshtml file that provides the top navigation bar

<div class="ms-NavBar">
<div class="ms-NavBar-openMenu js-openMenu"> <i class="ms-Icon ms-Icon--menu"></i>
</div>
<div class="ms-Overlay"></div>
<ul class="ms-NavBar-items">
<li class="ms-NavBar-item ms-NavBar-item--search ms-u-hiddenSm"> <div class="ms-TextField">
<input class="ms-TextField-field">
</div>
</li>
<li class="ms-NavBar-item"><a class="ms-NavBar-link" href="/Home/Index"> <i class="ms-Icon ms-Icon--home" aria-hidden="true"></i> Home</a>
</li>
<li class="ms-NavBar-item ms-NavBar-item--hasMenu"> <a class="ms-NavBar-link">Rendering Mode</a>
<i class="ms-NavBar-chevronDown ms-Icon ms-Icon--chevronDown" aria-hidden="true"></i>
<ul class="ms-ContextualMenu">
<li class="ms-ContextualMenu-item">
<a class="ms-ContextualMenu-link">Simple</a></li> <li class="ms-ContextualMenu-item">
<a class="ms-ContextualMenu-link">Normal</a></li> <li class="ms-ContextualMenu-item">
<a class="ms-ContextualMenu-link">Full</a></li>
</ul>
</li>
<li class="ms-NavBar-item"><a class="ms-NavBar-link" href="/Home/StartNewProcess">
<i class="ms-Icon ms-Icon--documentAdd" aria-hidden="true"></i> Start New Process</a></li>
<li class="ms-NavBar-item"><a class="ms-NavBar-link" href="/Home/MyProcesses">
<i class="ms-Icon ms-Icon--listCheckbox" aria-hidden="true"></i> List My Processes</a></li>
<li class="ms-NavBar-item"><i class="ms-Icon ms-Icon--globe" aria-hidden="true"></i> <a class="ms-NavBar-link" href="@BusinessApps.

O365ProjectsApp.WebApp.Components.O365ProjectsAppContext.CurrentSiteUrl">Back to
Target
Site</a></li>
@if (BusinessApps.O365ProjectsApp.WebApp.Components.MSGraphAPIContext. CurrentUserIsAdmin) {
<li class="ms-NavBar-item ms-NavBar-item--right"> <a class="ms-NavBar-link" href="/Home/Settings"> <i class="ms-Icon ms-Icon--gear" aria-hidden="true"></i> Settings</a></li>
}
</ul>
</div>

 

Notice the search text box, which leverages a hidden text box that will be shown if necessary. Also notice the fake Rendering Mode menu, which is made of three sub-menu items and is also highlighted with the --hasMenu style. Last, look at the conditional code, highlighted in bold, that checks if the current user is an Admin before showing the Settings menu item. The Settings menu is rendered on the right side of the screen by using the --right version of the menu item CSS class.

 

To work properly, the top navigation bar will also need to have a bunch of JavaScript code, which has already been added to the project by the Office UI Fabric NuGet package and has been included in that page through the script bundle named ~/bundles/fabric. Because you could have multiple navigation bars within a unique page, you also have to invoke the prototype JavaScript function called NavBar targeting the proper navigation bar. 

 

Furthermore, the menu items of the top navigation bar are branded with some fancy icons, which are available thanks to the Office UI Fabric project. At the time of this writing, there are about 338 custom icons that you can use in your projects to provide branding for menu items, buttons, and graphical elements in the UI of your software solutions. You can see the full list of available icons at the following URL: http://dev.office.com/fabric/styles#icons.

 

All the icons are based on a custom font that contains glyphs you can customize by changing their color, scale, and style. Every icon can be rendered by using an HTML syntax like the following:

<i class="ms-Icon ms-Icon--home" aria-hidden="true"></i>

 

The CSS class ms-Icon defines that the element represents an icon, and the ms-Icon--[Icon Name] class defines the specific kind of icon. The attribute aria-hidden with a value of true instructs any screen reader to skip the current icon, which is not text but just a glyphicon.

 

Responsive grid

So far, the UI of the custom Office 365 application has an Office 365 suite bar and a usable top navigation bar, but it would have awful page content and body—especially from a responsiveness perspective—unless you use the grid styles provided by Office UI Fabric.

 

The Office UI Fabric project provides a responsive grid made of up to 12 columns that behaves almost like the bootstrap grid. You just need to have a DIV element with the CSS class .ms-Grid and fill it with children DIV elements with the CSS class .ms-Grid-row. Each row can be made of one or more DIV elements styled with the CSS class .ms-Grid-col, followed by some other CSS classes that define how large the column will be on small, medium, and large devices. For the sake of completeness.

 

Row #1 Partitioned into three columns, each with a width of 4 blocks of 12

Row #2 Partitioned into four columns, each with a width of 3 blocks of 12

Row #3 Partitioned into two columns, the one on the left with a width of 4 blocks of 12 and the one on the right with a width of 8 blocks of 12

 

The styles applied to each column instruct the browser how to render the columns. The following is an explanation of the three kinds of styles:

ms-u-sm[size] Defines the size, with values between 1 and 12, for small-screen devices

ms-u-md[size] Defines the size, with values between 1 and 12, for medium-screen devices

ms-u-lg[size] Defines the size, with values between 1 and 12, for large-screen devices

Of course, you can have different column sizes based on the size of the device. For the sake of simplicity.

 

LISTING Sample HTML excerpt that renders a responsive grid of Office UI Fabric

<div class="ms-Grid">
<div class="ms-Grid-row">
<div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">First</div> <div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">Second</div> <div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">Third</div>
</div>
<div class="ms-Grid-row">
<div class="ms-Grid-col ms-u-sm3 ms-u-md3 ms-u-lg3">First</div> <div class="ms-Grid-col ms-u-sm3 ms-u-md3 ms-u-lg3">Second</div> <div class="ms-Grid-col ms-u-sm3 ms-u-md3 ms-u-lg3">Third</div> <div class="ms-Grid-col ms-u-sm3 ms-u-md3 ms-u-lg3">Fourth</div>
</div>
<div class="ms-Grid-row">
<div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">First</div> <div class="ms-Grid-col ms-u-sm8 ms-u-md8 ms-u-lg8">Second</div>
</div>
</div>

Based on what you learned in this section, you can now understand the shared CSHTML page layout.

 

Custom components and styles

The Office UI Fabric provides not only the responsive grid styles, the glyphicons, the Persona control, and the NavBar control, but also about 30 different controls that we can use to improve the user experience of our projects. Moreover, because it is an open source project with tens of committed people contributing within the worldwide community of Office 365 developers, the Office UI Fabric is a continuously evolving project.

 

It is fundamental to stress that all the Office UI Fabric controls are responsive and support three different sizes/formats: SmartPhone (small: max width of 320 pixels), Tablet (medium: max width of 640 pixels), and Desktop (large: max width up to 840 pixels).

 

Last but not least, in the Office UI Fabric you also find typography styles, which provide about 10 base font classes that allow you to adhere to the Office Design Language. Moreover, there are some CSS styles to use predefined color palette sets and animations for showing or hiding elements (enter/exit animations), for moving or sliding elements (move up, down, left, right), and to control the duration of the animations in the UI.

 

Note

You can find further details about the Office Design Language and the general UI guidelines for creating Office 365 applications and Office Add-ins at the following URL: https://dev.office.com/docs/add-ins/design/add-in-design.

 

Extending and consuming SharePoint Online

Now that the UI and UX of the application are consistent with the Office Design Language, we can concentrate on the real implementation and business logic.

First of all, we need to choose how the application can be activated. In the previous section, “Azure AD application general registration,” we saw that you can pin the application to the Office 365 app launcher. However, often the customers and end users want to be able to activate the application within the context of use and not only from a generic app launcher icon.

 

Let’s say for example that the end users want to be able to activate the application through the ECB (Edit Control Block) menu of any document in a specific document library of SharePoint Online so that the document will be the main and startup element for every team project.

 

Extending the UI of SharePoint Online

To satisfy the business requirement described in the previous paragraph, you can create a SharePoint Online list custom action that extends a target document library, defining a custom ECB menu item. Under the cover, that item will use JavaScript code to activate the custom Office 365 application and to start or access the team project.

 

In the old server-side code development and feature framework development for SharePoint, to create a list custom action you need to create an XML feature that defines all the attributes of the custom action. Now, the server side in SharePoint Online is not available, and the feature framework can be used only with a sandboxed solution. This is a deprecated habit if it contains code, and it is not suggested even if it is just a container for feature framework elements.

 

Within a SharePoint Add-in, you can use the client-side object model (CSOM) to interact with SharePoint and create artifacts and customization. However, the project we created is an Office 365 application, not a SharePoint Add-in. Thus, you may be wondering how you can create a custom action in SharePoint Online using an Office 365 application.

 

Note

If you want to read more about developing SharePoint Add-ins, you can read the book Microsoft SharePoint 2013 Developer Reference from Microsoft Press (ISBN: 0735670714), most of which is still valid for SharePoint 2016 in the field of SharePoint Add-ins development.

 

When we registered the application in Azure AD, we requested to have the application (app-only) permission to “Have full control of all site collections.” Thus, even if we are in an Office 365 application, we can use the SharePoint CSOM to create a list custom action onto a target site collection.

 

Moreover, if you are using the OfficeDev PnP Core library and the PnP Provisioning Engine, you can easily provision the custom action and the list to which the custom action applies. Thus, install the SharePointPnPCoreOnline NuGet package in the project. In Listing you can see a code excerpt that is executed when the custom application starts and authenticates against SharePoint Online with an app-only token. Then, it provisions a custom library with a custom action for the ECB menu of the documents in that library, in case the library does not exist yet.

 

LISTING Sample code excerpt that connects to SharePoint Online with an app-only token and provision some artifacts via CSOM

public static void Provision() {
Create a PnP AuthenticationManager object
AuthenticationManager am = new AuthenticationManager();
Authenticate against SPO with an App-Only access token
using (ClientContext context = am.GetAzureADAppOnlyAuthenticatedContext( O365ProjectsAppContext.CurrentSiteUrl, O365ProjectsAppSettings.ClientId, O365ProjectsAppSettings.TenantId,
O365ProjectsAppSettings.AppOnlyCertificate)) {
Web web = context.Web;
List targetLibrary = null;
If the target library does not exist (PnP extension method) if (!web.ListExists(O365ProjectsAppSettings.LibraryTitle)) {
// Create it using another PnP extension method
targetLibrary = web.CreateList(ListTemplateType.DocumentLibrary, O365ProjectsAppSettings.LibraryTitle, true, true);
}
else {
targetLibrary =
web.GetListByTitle(O365ProjectsAppSettings.LibraryTitle);
}
If the target library exists if (targetLibrary != null) {
Try to get the user's custom action
UserCustomAction customAction =
targetLibrary.GetCustomAction(O365ProjectsAppConstants.ECB_Menu_N
// If it doesn't exist
if (customAction == null) {
Add the user custom action to the list customAction = targetLibrary.UserCustomActions.Add(); http://customAction.Name = O365ProjectsAppConstants.ECB_Menu_Name; customAction.Location = "EditControlBlock"; customAction.Sequence = 100;
customAction.Title = "Manage Business Project"; customAction.Url = $"
{O365ProjectsAppContext.CurrentAppSiteUrl}Project/?Si
teUrl={{SiteUrl}}&ListId={{ListId}}&ItemId={{ItemId}}&ItemUrl={{ItemUrl}}";
}
else {
Update the already existing Custom Action http://customAction.Name = O365ProjectsAppConstants.ECB_Menu_Name; customAction.Location = "EditControlBlock"; customAction.Sequence = 100;
customAction.Title = "Manage Business Project"; customAction.Url = $"
{O365ProjectsAppContext.CurrentAppSiteUrl}Project/?Si
teUrl={{SiteUrl}}&ListId={{ListId}}&ItemId={{ItemId}}&ItemUrl={{ItemUrl}}";
}
customAction.Update();
context.ExecuteQueryRetry();
}
}
}

 

Notice the use of the AuthenticationManager class, which is part of the PnP Core library and provides some helper methods to create a CSOM ClientContext object using any of the available authentication techniques. In Listing the GetAzureADAppOnlyAuthenticatedContext method uses the target URL of the site collection, the ClientId and TenantId defined in Azure AD, and the X.509 certificate that we registered in the section “App-only authorization registration” earlier in this blog to create an authenticated context using an app-only access token.

 

The code sample hides the complexity of retrieving the X.509 certificate to authenticate against Azure AD through a global setting called O365ProjectsAppSettings.AppOnlyCertificate. In the full source code of the sample application, you will find the details about how to retrieve the certificate from a certificate store.

 

After creating an authenticated context, the code excerpt checks if the target library exists by using the ListExists extension method, which is available in the PnP Core library. If the list exists, the code gets a reference to it using another extension method called GetListByTitle. Otherwise, it creates the library using the CreateList extension method.

 

Regardless of whether the list already exists, after getting a reference to the library the code checks if the target library already has a user’s custom action for the ECB menu. If the custom action does not exist, the code creates a new one. Otherwise, it updates the existing one.

 

The ECB menu item will drive the user’s browser to the Office 365 application, providing in the query string the URL of the current document together with its ListIdItemId, and the source site URL, leveraging the classic SharePoint URL tokens.

 

As you can see, the user’s custom action creation requires you to invoke the ExecuteQuery method of the CSOM ClientContext object. However, Listing uses another PnP Core library extension method called ExecuteQueryRetry, which is a powerfull method that internally handles any retry of the ExecuteQuery standard method of the ClientContext object.

 

When you target SharePoint Online, some requests could fail or be rejected because of connectivity issues or throttling rules on the services side. The ExecuteQueryRetry method provided by PnP automatically handles retries and hides from your code the need to take care of any connectivity or throttling issues. You can even customize the ExecuteQueryRetry method, providing arguments for retryCount and delay between retries. By default, the

 

ExecuteQueryRetry method will retry 10 times, with a 500 milliseconds delay between each retry.

 

Provisioning SharePoint artifacts

In the previous section, you saw how to create a library and a user’s custom action in the target site by using CSOM and the PnP Core library. However, within the PnP Core library you can also find the PnP Remote Provisioning Engine. Through that engine, you can do remote provisioning by code or by applying a template file, which can be applied by using PowerShell or by writing .NET code.

 

For the sake of completeness, in this section you will see how to leverage an XML-based template file to provision artifacts by writing just a few lines of code. In Listing, you can see a PnP XML provisioning template that provisions the library that we discussed earlier in this blog and its user’s custom action.

 

LISTING Sample PnP XML provisioning template that provisions a library with a user’s custom action

<?xml version="1.0"?>
<pnp:Provisioning xmlns:pnp="http://schemas.dev.office.com/PnP/2016/05/ ProvisioningSchema">
<pnp:Preferences Generator="OfficeDevPnP.Core, Version=2.5.1606.2,

Culture=neutral, Publ

icKeyToken=3751622786b357c2">
<pnp:Parameters>
<pnp:Parameter Key="AppSiteUrl" Required="true" />
</pnp:Parameters>
</pnp:Preferences>
<pnp:Templates ID="CONTAINER-TEMPLATE-O365ProjectsApp"> <pnp:ProvisioningTemplate ID="TEMPLATE- O365ProjectsApp" Version="1">
<pnp:Lists>
<pnp:ListInstance Title="BusinessProjects" Description="" DocumentTemplate="{site}/BusinessProjects/Forms/template.dotx" TemplateType="101" Url="BusinessProjects" EnableVersioning="true" EnableMinorVersions="true" MinorVersionLimit="0" MaxVersionLimit="0" DraftVersionVisibility="0" TemplateFeatureID="00bfea71-e717-4e80-aa17-d0c71b360101" EnableAttachments="false">
<pnp:ContentTypeBindings>
<pnp:ContentTypeBinding ContentTypeID="0x0101" Default="true" /> <pnp:ContentTypeBinding ContentTypeID="0x0120" />
</pnp:ContentTypeBindings>
<pnp:Views>
<View Name="{632CEDCA-76C7-4C0E-AEED-4D343DB02B5B}" DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" Type="HTML" DisplayName="All

Documents" Url="/

sites/O365ProjectsAppSite/BusinessProjects/Forms/AllItems.aspx" Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/15/images/dlicon.png?rev=43"> <Query>
<OrderBy>
<FieldRef Name="FileLeafRef" />
</OrderBy>
</Query>
<ViewFields>
<FieldRef Name="DocIcon" />
<FieldRef Name="LinkFilename" />
<FieldRef Name="Modified" />
<FieldRef Name="Editor" />
</ViewFields>
<RowLimit Paged="TRUE">30</RowLimit>
<JSLink>clienttemplates.js</JSLink>
</View>
</pnp:Views>
<pnp:FieldRefs>
<pnp:FieldRef ID="ef991a83-108d-4407-8ee5-ccc0c3d836b9" Name="SharedWithUsers" DisplayName="Shared With" />
<pnp:FieldRef ID="d3c9caf7-044c-4c71-ae64-092981e54b33" Name="SharedWithDetails" DisplayName="Shared With Details" />
<pnp:FieldRef ID="3881510a-4e4a-4ee8-b102-8ee8e2d0dd4b" Name="CheckoutUser" DisplayName="Checked Out To" />
</pnp:FieldRefs>
<pnp:UserCustomActions>
<pnp:CustomAction Name="O365ProjectsApp.ManageBusinessProject" Location="EditControlBlock" Sequence="100" Rights="EditListItems,AddListItems,DeleteListItems" Title="Manage Business Project" Url="

{AppSiteUrl}/Project/?

SiteUrl={SiteUrl}&amp;ListId={ListId}&amp;ItemId={ItemId}&amp;ItemUrl=

{ItemUrl}" Enabled="true" />
</pnp:UserCustomActions>
</pnp:ListInstance>
</pnp:Lists>
</pnp:ProvisioningTemplate>
</pnp:Templates>
</pnp:Provisioning>

 

The template file declares to provision a new library by using the ListInstance element highlighted in bold with a user’s custom action, which is also highlighted in bold. Moreover, the template accepts a mandatory parameter named AppSiteUrl, which you can see at the beginning of the template, and which allows you to keep the URL of the site publishing the Office 365 application dynamic.

In Listing you can see how to apply that provisioning template to a site, creating the artifacts or updating them if they already exist.

 

LISTING Sample code excerpt that applies a provisioning template to a site

public static void Provision() {
Create a PnP AuthenticationManager object AuthenticationManager am = new AuthenticationManager();
Authenticate against SPO with an App-Only access token
using (ClientContext context = am.GetAzureADAppOnlyAuthenticatedContext( O365ProjectsAppContext.CurrentSiteUrl, O365ProjectsAppSettings.ClientId, O365ProjectsAppSettings.TenantId,
O365ProjectsAppSettings.AppOnlyCertificate)) {
Web web = context.Web;
Load the template from the file system XMLTemplateProvider provider =
new XMLFileSystemTemplateProvider( String.Format(HttpContext.Current.Server.MapPath(".")), "ProvisioningTemplates");
ProvisioningTemplate template = provider.GetTemplate("O365ProjectsAppSite.xml");
Configure the AppSiteUrl parameter template.Parameters["AppSiteUrl"] =
O365ProjectsAppContext.CurrentAppSiteUrl;
Apply the template to the target site template.Connector = provider.Connector; web.ApplyProvisioningTemplate(template);
}
}

 

Notice the statement that loads the PnP provisioning template from the file system and then configures the current site URL, providing a value for the required template parameter. Last, by using the ApplyProvisioningTemplate extension method, the code applies the template to the target site.

 

Because internally the PnP Remote Provisioning Engine does delta handling and compares the target site with the source template, this technique will always keep the target site in sync and aligned with the requirements and capabilities defined in the application within the provisioning XML file.

 

Note

For further details about the XML schema available for defining PnP provisioning templates, you can browse the GitHub repository where the schema is defined and available as a community open source project: https://github.com/OfficeDev/PnP-Provisioning-Schema/

 

Consuming SharePoint Online with delegated permissions

The initial provisioning of artifacts most likely will have to be done using app-only credentials because it requires high permission on the target SharePoint Online. Because the OAuth 2.0 authorization protocol intersects the app permissions with the current user’s permissions, you cannot provision artifacts with every kind of user, and you can’t assign full control permissions to all users, either.

 

Thus, having the capability to act as app-only, only when needed, is powerful. We could say that the app-only technique, in the context of the Office 365 application model and in the SharePoint Add-in model, is like the SPSecurity.RunWithElevatedPrivileges of the old server-side development in SharePoint 201x, but with much more control on the permissions assigned to the app that will act as app-only.

 

However, in common tasks you probably will need to act with delegated permissions based on the currently logged-in user. In Listing, you can see a code excerpt that shows how to consume SharePoint Online via CSOM using the current user’s identity and an OAuth 2.0 access token with both the user’s token and the app token.

 

LISTING Sample code excerpt that consumes SharePoint Online via CSOM using an OAuth 2.0 access token with user’s token and app token

public static void BrowseLibraryFiles() {
Create a PnP AuthenticationManager object AuthenticationManager am = new AuthenticationManager();
Authenticate against SPO with an App-Only access token using (ClientContext context =
am.GetAzureADWebApplicationAuthenticatedContext( O365ProjectsAppContext.CurrentSiteUrl, (url) => {
return (MicrosoftGraphHelper.GetAccessTokenForCurrentUser(url)); })) {
Web web = context.Web;
var targetLibrary =
web.GetListByTitle(O365ProjectsAppSettings.LibraryTitle);
context.Load(targetLibrary.RootFolder,
fld => fld.ServerRelativeUrl,
fld => fld.Files.Include(f => f.Title, f => f.ServerRelativeUrl)); context.ExecuteQueryRetry();
foreach (var file in targetLibrary.RootFolder.Files) { // Handle each file object
}
}
}

The sample code excerpt creates a ClientContext instance object authenticated against Azure AD and based on the current user’s authorization context by using the

 

AuthenticationManager class of PnP and leveraging its GetAzureADWebApplicationAuthenticatedContext method.

Notice that the above code will access the contents with a delegated permission. Thus, it will have access only to those contents that are accessible to both the application, based on its Azure AD delegated permissions, and the currently logged-in user. Because in Azure AD the application has the delegated permission “Read and write items and lists in all site collections”, the current user’s permissions will determine the effective and resulting permissions.

 

Whether you want to use delegated permissions or app-only access tokens, by using the techniques illustrated in this and the previous section and having proper permissions in Azure AD and in SharePoint Online, you can do almost everything you need against SharePoint Online by using CSOM and the PnP extension methods and helper classes.

 

Using the Microsoft Graph

In the first section of this blog, you saw that creating an Office 365 application enables you to consume not only SharePoint Online, but also the entire Office 365 ecosystem. 

 

In this section, you will learn how to apply what you have learned in theory to a real use case. In this section, the key point is how to leverage the OAuth access token to consume the Microsoft Graph API, not the specific actions executed. Nevertheless, discussing the potential and the capabilities through real examples makes it easier to understand the topic and to follow the flow.

 

Creating and consuming the project’s Office 365 Group

One requirement of the business use case to which the sample Office 365 application refers is to create a new Office 365 Group for each project that has to be managed. In the code samples, you will find the full implementation of the solution. Here, we will discuss just what really matters from a learning perspective.

 

In Listing you can see a code excerpt of a helper method that creates a new Office 365 Group, assigns some users as members of the just-created group, and optionally updates the image of the group.

 

LISTING Helper method that uses the Microsoft Graph API to create and configure an Office 365 Group

<summary>
Creates a new Office 365 Group for a target Project
</summary>
<param name="group">The group to create</param>
<param name="membersUPN">An array of members' UPNs</param>
<param name="photo">The photo of the group</param>
<returns>The Office 365 Group created</returns>
public static Group CreateOffice365Group(Group group, String[] membersUPN, Stream photo = null) {
// Create the Office 365 Group
String jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}groups",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri), group, "application/json");
var addedGroup = JsonConvert.DeserializeObject<Group>(jsonResponse);
// Set users' membership
foreach (var upn in membersUPN) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}groups/{1}/members/$ref", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, http://addedGroup.Id),
new GroupMemberToAdd {
ObjectId = String.Format("{0}users/{1}/id", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn)
},
"application/json");
}
Update the group's picture, if any if (photo != null) {

 

Retry up to 10 times within 5 seconds, because the

Office 365 Group sometime takes long to be ready Int32 retryCount = 0;
while (true) { retryCount++; try {
if (retryCount > 10) break; System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(500));
photo.Position = 0;
MemoryStream photoCopy = new MemoryStream(); photo.CopyTo(photoCopy); photoCopy.Position = 0;
MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}groups/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://addedGroup.Id),
photoCopy, "image/jpeg");
break;
}
catch {
// Ignore any exception, just wait for a while and retry
}
}
}
return (addedGroup);
}

As you can see, all the tasks are handled by making some HTTPS direct requests against the Microsoft Graph API endpoints. From an authorization perspective, the code leverages the delegated permissions of type “Read and write directory data,” and “Read and write all groups,” which means that the user invoking the function must have at least the same permissions.

 

Notice also that the upload of the custom image for the group implements a retry logic because often the image is available as a target endpoint a few milliseconds after the creation of the group. The group creation method returns the just-created group, which can be useful for further use.

 

In the sample project, whenever a user clicks the ECB menu item Manage Business Project, which has been created in the section “Extending and consuming SharePoint Online” earlier in this blog, the MVC controller that will handle the request will check if the Office 365 Group backing the project exists. If the group exists, the controller will consume it, providing to the end user some high-level information about the group. If the group does not exist, the controller will use the method illustrated in Listing  to create the group.

 

In Listing, you can see a code excerpt used to test whether the Office 365 Group exists.

 

LISTING Helper method that uses the Microsoft Graph API to check if an Office 365 Group exists

<summary>
Checks whether an Office 365 Group exists or not
</summary>
<param name="groupName">The name of the group</param>
<returns>Whether the group exists or not</returns>
public static Boolean Office365GroupExists(String groupName) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups?$select=id,displayName" +
"&$filter=groupTypes/any(gt:%20gt%20eq%20'Unified')%20" + "and%20displayName%20eq%20'{1}'",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, HttpUtility.UrlEncode(groupName).Replace("%27", "''")));
var foundGroups = JsonConvert.DeserializeObject<GroupsList>(jsonResponse); return (foundGroups != null && foundGroups.Groups.Count > 0);
}

 

The sample method makes a REST request against the collection of groups in the current tenant, filtering the items based on the values in the groupTypes collection and searching the target group by display name. If the response is a collection of at least one item, the group does exist; otherwise, it doesn’t.

 

Sending notifications on behalf of users

After creating the Office 365 Group for the project, you may want to send an email message to the group members by leveraging the conversation panel of the just-created group.

 

Using the current user’s context of authentication, you can automate the sending of the message on behalf of the current user. The group will get a message from the current user, but in reality the application will send the message automatically. To achieve the described result, the application must have proper permissions in Azure AD, as discussed at the beginning of this blog. In Listing, you can see a code excerpt that sends a new thread message to the conversation stream of the group.

 

LISTING Helper method that uses the Microsoft Graph API to send a message to an Office 365 Group

<summary>
Creates a new thread in the conversation flow of a target Office 365 Group
</summary>
<param name="groupId">The ID of the target Office 365 Group</param> public static void SendMessageToGroupConversation(String groupId) {
var conversation = new Conversation {
Topic = "Let's manage this Business Project!", Threads = new List<ConversationThread>(
new ConversationThread[] {
new ConversationThread {
Topic = "I've just created this Business Project", Posts = new List<ConversationThreadPost>(
new ConversationThreadPost[] {
new ConversationThreadPost {
Body = new ItemBody {
Content = "<h1>Welcome to
Project</h1>",
Type = BodyType.Html,
},
}
})
}
})
};
MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}groups/{1}/conversations", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId), conversation, "application/json");
}

 

The key part of the code sample is the creation of the new Conversation object that is made of a new ConversationThread. As you can see in Figure the result will be the creation of a new welcome thread in the new Office 365 Group.

 

Creating asynchronous jobs

As you probably experienced while playing with the examples of the previous section, the creation of an Office 365 Group can take a few seconds because under the cover it will create a SharePoint Online site collection and some other objects in Azure AD and Exchange Online.

 

In general, many tasks can take quite a long time to complete or just an unpredictable amount of time to run whenever you interact with external services that are provided by a third party or by a cloud-based platform. Moreover, in Office 365 there are throttling rules that may cause some of your requests to fail, as you saw with SharePoint Online when talking about the benefits of using the ExecuteQueryRetry method of PnP compared with the standard ExecuteQuery.

 

Furthermore, while consuming such API and services within a web-based application, the HTTP requests from the web browser (that is, the client) to the web server that is providing the functionality can time out while waiting for back-end services.

 

For example, if you host your ASP.NET MVC web application in Microsoft Azure, by default you will have one minute of request timeout. What if you want or need to manage requests that can take longer than one minute? In general, what can you do to have a stronger and more available software architecture, avoiding tight dependancies on unpredictable completion times of third-party services?

 

There are some good patterns that make your software architectures more solid. One of the most powerful patterns is the asynchronous job, which will be discussed in the following paragraphs.

 

Remote timer job architecture

The basic idea of the asynchronous job pattern is to avoid executing actions that are complex, long-running, or have an unpredictable completion time in the foreground and in real time, instead leveraging a queue of actions that will be performed in the background by an engine (a worker job) as soon as there are resources available for processing them.

 

Many enterprise-level platforms and solutions have an out-of-box system to provide such functionalities. For example, think about the TimerJob framework of SharePoint on-premises. However, the TimerJob framework of SharePoint requires a full trust code development approach, which is not suitable for Office 365. In Office 365, there isn’t a scheduler service for executing background actions or tasks.

 

Nevertheless, as you have seen in many other situations throughout this book, one of the best friends of Office 365 is Microsoft Azure. By using Microsoft Azure, you can create WebJobs that can be scheduled to run at a specific time based on a schedule or can be executed on demand manually by users or triggered by some external event. One of the possible events that can be used to trigger an Azure WebJob is the presence of a message in an Azure Storage Queue.

 

Thus, whenever you are in an Office 365 application and need to perform an action that is business critical and can take an unpredictable amount of time to run, you can enqueue into an Azure Storage Queue a request to perform that action. Later, an Azure WebJob will be triggered and will perform the real action in the background. In Figure you can see an architectural schema of the overall solution.

 

The job is often referred to as a “remote timer job” because it mimics a classic SharePoint timer job, but it acts remotely by using the Microsoft Graph and the SharePoint REST API instead of any server-side code. Moreover, it can be a job that interacts with the entire Office 365 ecosystem using the Office 365 application model.

 

From an authentication perspective, the job can access the Office 365 services either by using a set of application credentials (username and password) or—even better—by using a ClientID and SharedSecret by leveraging the OAuth 2.0 protocol and Azure AD, typically running in an app-only authorization context.

 

If you are targeting SharePoint Online only, you can even consider using the PnP remote timer job framework, which is part of the PnP Core library and provides some useful types (base abstract classes and helper types) to make it easier for you to create a remote timer job for SharePoint with this new model.

 

For example, you could have a SharePoint Online remote timer job to check the security settings of sites. In fact, it is a good habit and a best practice to have at least two site collection administrators for each site collection, but SharePoint Online requires only one site collection administrator.

 

Thus, you can create a SharePoint remote timer job that can go through all the existing site collections of a tenant and double-check the number of site collection administrators, adding a predefined second one where there is only one or sending an alert to the single site collection administrator. This is a perfect candidate for using the PnP remote timer job framework and for targeting SharePoint Online only. In the PnP Partner Pack sample solution, there is this kind of sample remote timer job for SharePoint Online.

 

However, there are many scenarios in which you will need to target the entire Office 365 ecosystem. For example, what we did in the previous section—creating an Office 365 Group, setting its image, and enabling members of the group—is a good candidate for such an asynchronous task.

 

Creating a remote timer job in Azure

Let’s see how you can create a real remote timer job hosted in Azure that will create and configure Office 365 Groups with the asynchronous pattern described in the previous section.

 

First of all, you have to create a Microsoft Azure Blob Storage account that will host the Blob Queue. To do this, open the Microsoft Azure management portal (https://portal.azure.com/) and select a target subscription, which will be used for the required services. From the left menu of the portal, you can select Storage Accounts and then select to Add A New Storage Account. Provide all the required information—the storage account name, the performance level, the deployment model, and so on.  

 

Publishing your applications and add-ins Once you have created an Office Add-in, a SharePoint Add-in, or a Microsoft Office 365 web application, you most likely will need to make it available to your target users. In this blog, you will learn how to publish your solution either on a private Corporate Catalog or in the public Office Store.

 

Options for publishing add-ins and web applications

In this section, you will learn about the options for publishing an application and making it available to the users of your Microsoft Office 365 tenant.

Although the options illustrated in this section target only Office 365 scenarios, you should be aware that the SharePoint Add-ins, Office Add-ins, and Office 365 web applications that you create can also be consumed in on-premises scenarios. In fact, you could have an Office 365 hybrid topology, where you share some services and custom applications across your on-premises infrastructure and the tenant in Office 365. You could also develop a custom solution that targets on-premises only.

 

The development model for Office Add-ins and SharePoint Add-ins is the same for both on-premises and the cloud, aside from the authentication model. In Office 365, you rely on Microsoft Azure Active Directory (Azure AD) and Microsoft Azure Access Control Services (ACS); by default, on-premises you rely on Windows Active Directory.

 

The only exceptions are the Office 365 applications, which by definition are required to be registered on Azure AD and therefore cannot be used in an on-premises only environment. Last but not least, remember that even if you have an on-premises SharePoint environment, you can register Azure ACS as a trusted authority and use the same authentication model that you have in SharePoint Online. This last approach is useful when you are in a hybrid topology.

 

Private corporate publishing

The first option you have is to publish your custom applications using a Corporate Catalog in SharePoint Online or using Exchange Online if your application is an Outlook Add-in.

 

This is the most common scenario: you have a custom, tailor-made solution that targets your environment or your company only. If the target is your tenant only, you wouldn’t publish such a solution on the public Office Store. In this scenario, the most interesting option is to leverage the Corporate Catalog site collection of SharePoint Online because out of the box you will have all the capabilities of the store, even if users will not pay for installing your add-ins.

 

To publish such a solution, you first need to have a Corporate Catalog site in SharePoint Online. As a developer, you shouldn’t have to create a Corporate Catalog by yourself, at least not in your production environment. However, you may need to create one in your development environment. If you don’t have a Corporate Catalog, open the browser and access the SharePoint Admin Center. Select the Apps menu item on the left, and on the right side of the page, choose the option to access the app catalog.

 

From the app catalog site page, you will be able to create a new app catalog site or provide the URL of an existing site, if any. To create a new app catalog, click the OK button on the lower-right side of the page. You will be prompted with a form to provide some information about the app catalog that will be created.

 

This information includes the site title, the relative URL, the default language, the time zone, the primary site collection administrator, and the server quota resources. After providing this information, click the OK button to create the app catalog. Wait for a while, and your Corporate Catalog will be ready. Keep in mind that the Corporate Catalog in Office 365 is unique for the tenant, so all the add-ins you will publish there will be visible to all of your site collections.

 

Once you have a Corporate Catalog, browse to its URL. If you have proper permissions, you will be able to add a new Office Add-in by uploading its XML manifest file into the Apps for Office library or a new SharePoint Add-in by uploading the .APP file into the Apps for SharePoint library. For every add-in, you will be able to provide information like the title, the icon, the version, the supported languages, some screenshots, and so on.

 

Note

By default, the app catalog can be read by any member of the group Everyone Except External Users. Moreover, the site collection administrator designated when the site is created will have full control of the site contents. Of course, you can add more users with specific permissions for publishing Office Add-ins or SharePoint Add-ins.

 

If you are publishing an Office Add-in, you will have to register the app catalog in the Office client applications through the Trust Center of the applications. You can find detailed instructions about how to accomplish this task in the document “Publish task pane and content add-ins to an add-in catalog on SharePoint,” which is available at the following URL: dev.office.com/docs/add-ins/publish/publish-task-pane-and-content-add-ins-to-an-add-in-catalog. If you like, you can also configure the app catalog URLs of the Trust Center in Office client by using a Group Policy in Windows Active Directory.

 

After you have published an add-in in the Corporate Catalog, you and your tenant users will be able to use it. If you published a SharePoint Add-in, browse to the Site Contents page of the site where you want to install the add-in and select Add An App, choose the add-ins coming From Your Organization, and you will find the SharePoint Add-ins that you published in the app catalog. If you published an Office Add-in, open the target Office client application and select Insert A New Item From The Store. Choose the My Organization tab and search for your tailor-made add-ins.

 

If you want to update your add-in, you will need to upload an updated version of the file into the target library, increasing the version number, and everything else will be managed by the infrastructure of the add-in.

 

Office Store

Another option you have is to publish your add-ins or applications to the public marketplace, making them available to everyone through the Office Store. Before being listed in the Office Store, your add-ins or applications will have to adhere to some strict validation rules.

 

See Also

For details about the rules to which your add-ins and applications must adhere, you can read the document “Validation policies for apps and add-ins submitted to the Office Store,” which is in version 1.9 at the time of this writing. It is available at the following URL on MSDN: msdn.microsoft.com/en-us/library/office/jj220035.aspx.

 

To publish a solution on the Office Store, you will have to create an account on the Microsoft Seller Dashboard. More details about this procedure are provided in the following section, “Using the Seller Dashboard.”

 

After you have submitted your solution to the Seller Dashboard and it has been validated and approved by Microsoft, your users will be able to find and install (and possibly buy) your add-ins or applications directly from the Office Store. We say “possibly buy” because you can provide solutions for free and still list them in the public marketplace.

 

File share publishing

Last but not least, you can publish an Office Add-in through a network file share. This option is not suitable for publishing SharePoint Add-ins or Office 365 applications.

 

If you want, you can copy the XML manifest file of an Office Add-in into a network share and install it directly from there. You will have to configure the network share as a corporate app catalog in the Trust Center of Office client. You can find detailed instructions about how to accomplish this task in the document “Create a network shared folder catalog for task pane and content add-ins,” which is available at the following URL: http://dev.office.com/docs/add-ins/publish/create-a-network-shared-folder-catalog-for-task-pane-and-content-add-ins.

 

This a useful scenario when you want to test your add-ins during the development process without publishing the add-ins in any catalog. It can also be useful in the case of a released add-in that you want to consume with only a small number of Windows desktop clients. For example, if you have a small company, you could avoid creating a Corporate Catalog or registering your add-ins in the Office Store by installing them through a network share.

 

Using the Seller Dashboard

The most interesting scenario is publishing an add-in or an Office 365 application by using the Office Store. Although the process is straightforward, it’s worth the time to explain it in detail, as you will see in this section.

 

First of all, to publish any kind of solution, you will need a Microsoft account to access the Seller Dashboard, which is available at the following URL: go.microsoft.com/fwlink/ Moreover, you will have to register a Microsoft developer account in the Microsoft DevCenter, if you don’t have one yet. You can find further information about how to create a Microsoft developer account at the following URL: Register as an app developer.

 

Before submitting a solution for approval in the Seller Dashboard, you should double-check the list of requirements Microsoft has defined and made available in the document “Checklist for submitting Office and SharePoint Add-ins and Office 365 web apps to the Seller Dashboard,” which is available at the following URL: Use the Seller Dashboard to submit your solution to AppSource.

 

The process can vary, depending on the kind of solution you are going to publish. However, the beginning of the publishing flow is the same, regardless of what kind of application you are going to publish.

 

Starting from the page you need to select the red Continue button under the Office column, and you will be prompted with a list of solutions that you have already published in the Office Store or that are in the process of being published. You can click one of the existing apps to update it or complete any draft registration.

 

To register a new application, select the Add A New App button. You will be prompted with a wizard that will ask you what kind of app you are going to publish. The wizard step will be titled Listing Type, and the available choices, at the time of this writing, are:

 

  • Office Add-in This option allows you to publish an Office Add-in like a Task Pane Add-in or a Content add-in for Word, PowerPoint, or Excel.
  • Outlook Add-in This option allows you to publish an Outlook Add-in like a mail app.
  • SharePoint Add-in This option allows you to publish a SharePoint Add-in, whether it is a SharePoint-hosted or a provider-hosted solution.
  • Web App Using Azure AD This option allows you to publish an Office 365 application registered in Azure AD

 

The following paragraphs detail all the provided options.

 

Publishing Office Add-ins

If you are publishing an Office Add-in, you have to choose one of the first two options in the Listing Type wizard step. Let’s choose the Office Add-in type. The following steps will allow you to provide much more detailed information about the add-in. In the first wizard step, called Overview, you will have to provide the package of the add-in, which will be the XML manifest file, together with some general information about the add-in such as the following:

 

Submission Title This is the title of the add-in as it will be shown in the Office Store.

Version This is the release version of the add-in. It is automatically populated from the manifest file. This field becomes useful when you need to release updates of a published add-in.

 

Release Date (UTC) This defines the release date of the add-in in the Office Store. Even if Microsoft approves your add-in before that release date, the add-in will only be available in the Office Store starting from that date. If you are publishing an updated version of the add-in, the currently released version of the add-in will not be available in the Office Store until the new version is approved and released. Of course, customers who already installed your current version of the add-in will be able to continue using it.

 

Category You have to provide at least one category for your add-in, and you can provide up to three categories. Those categories will help customers find your add-in while searching the Office Store.

 

Testing Notes This is a text field for internal use only between you and the team that will test the add-in before releasing it to the Office Store. Here you can put notes, comments, hyperlinks to resources, or hints that will help the testers do proper testing of your add-in. This information will not be published in the Office Store and will be used only by the testing team.

 

You will also have the capability to declare that your app makes calls, supports, contains, or uses cryptography or encryption.

You must choose if you will make your add-in available in the catalog for iPad. This will imply further testing and compliancy requirements. If you choose to make it available, you will also have to provide your Apple developer ID in the registration form.

 

Logo This is the logo that will be used for your add-in in the Office Store. It can be a PNG, JPG, JPEG, or GIF with a size of exactly 96 × 96 pixels and a file size not greater than 250 KB.

 

  • Support Document Link This is the full URL of a document that provides instructions to users who have issues with your add-in.
  • Privacy Document Link This is the full URL of a document that provides privacy information to users of your add-in.
  • Video Link This is an optional field that can contain the full URL of a video file about your add-in.

 

End User License Agreement Through this field, you can provide an optional end user license agreement (EULA). It can be a file in any of the following formats: DOC, DOCX, RTF, TXT, or PDF. If you do not provide an EULA, a default one will be provided on your behalf.

 

Publishing an Outlook Add-in instead of an Office Add-in will prompt you for the same set of fields.

 

In the second wizard step, called Details, you can provide additional information about the languages supported by the add-in and all the text messages and images specific to a particular language. The following is a list of fields and settings that you have to provide in this second wizard step:

 

Language Allows you to select the current language for which you are providing the detailed settings. By default, every add-in has at least the English language, but there could be many additional languages.

  • App Name This is the name of the add-in in the selected language.
  • Short Description Defines a short description for your add-in in the currently selected language.
  • Long Description Provides a long description for the add-in in the current language.
  • Screenshots Allows you to provide up to five sample screenshots of the add-in. At least one screenshot is mandatory; the remaining four are optional.

 

To add any new language aside from the English language, click the Add A Language button, which is available in the lower side of this wizard step. A pop-up screen allows you to select as many languages as you need.

 

The third wizard step is called Block Access, which allows you to block customers in certain countries or regions from purchasing or even using the add-in. The last wizard step is Pricing, which allows you to define the pricing model for your add-in. You have three options from which to choose:

 

  • This App Is Free The app is available for free, indeed.
  • This App Is For One-Time Purchase Customers pay for the app only once.
  • This App Will Be Sold As A Subscription You want to sell the app according to the Software as a Service (SaaS) model.

 

If you want to provide the app for free, later you will be able to change the pricing model and convert the app to SaaS or to a one-time purchase mode. If you are not providing the app for free, you will have to choose a pricing model. If you select to sell the add-in using a one-time purchase model, you will later be able to convert the app to the free model, but you cannot convert it to a subscription model. If you select to sell the add-in with a subscription model, you cannot change it to free or one-time purchase model after it has been published.

 

The price, if any, has to be declared on a per-user basis, through the Price Per User field, and you cannot define whatever price you like. There is a long list of available prices ranging from $1.49 to $999.99, and the available prices are predefined. You can also select a Price Threshold field, which enables you to declare a maximum number of per-user licenses for which you want a single customer to pay. After that number, any further per-user license will have no additional charge. You can select No Threshold or any of the predefined values like 1, 10, 25, ..., up to 1,000.

 

Here are some example scenarios:

If you select a per-user price of $9.99 and a price threshold of 10, your customers will pay $9.99 for each user up to 10 users. Starting from the eleventh user, they will not pay any more.

If you select a per-user price of $9.99 and no price threshold, your customers will pay $9.99 for every user.

If you select a per-user price of $9.99 and a price threshold of 1, your customers will pay $9.99 just once, and the result will be like a per-site license.

 

Note

To sell an app, you will also have to configure a valid tax account in the Seller Dashboard. Otherwise, you will not be able to publish the add-in in the Office Store because you wouldn’t be able to get your money back from Microsoft.

 

You can also enable trial support for your add-in by enabling the option with label My App Supports A Trial. If you enable a trial, you have to specify the Duration Of Trial, which can assume any of the following values: 15 days, 30 days, 60 days, or Unlimited. One more setting related to the trial of your app is the Number Of Users In Trial option, which can assume any of the following values: 1 user, 5 user(s), 20 user(s), or Unlimited. Once you have configured all of the settings for the add-in, you can save it as a draft for further editing by clicking the SAVE AS DRAFT button, or you can submit it for publishing by clicking the SUBMIT FOR APPROVAL button.

 

After submitting an add-in, your solution will go through the validation process and eventually be approved and published in the Office Store, based on the publishing date that you provided.

 

Publishing SharePoint Add-ins

The process of publishing a SharePoint Add-in is similar to the process of publishing an Office Add-in. However, there are some differences within the fields and settings that you have to provide.

 

In the Overview first step of the publishing wizard, you will have to provide a package for a SharePoint Add-in, which has an extension of .APP and which is the only supported one at the time of this writing. Moreover, you will have to provide the version number manually instead of having it automatically populated from the XML manifest file, like with the Office Add-ins.

 

Furthermore, if your add-in needs to act as a service, using server-to-server communication, you will have to declare this need by flagging the field called My App Is A Service And Requires Server To Server Authorization. This option will require you to link the current add-in with a ClientID that you can create using the Seller Dashboard by applying the following procedure.

 

First of all, to create a new Client ID, you have to select the Client IDs tab in the Seller Dashboard and click the Add A New OAuth Client ID button. You will be prompted with a two-step wizard. The first step will ask you to provide details about the app for which you want to register the Client ID.

 

 

Friendly Client ID Name This is a name to help you recognize the app later.

App Domain Defines the domain on which your app will run. It has to be a valid domain name, without the http:// or https:// trailer.

App Redirect URL Declares the URL to which end users will be redirected after accepting the authorization requirements for your app. It has to be a valid URL, including the trailer http:// or https:// protocol.

Client Secret Valid For Allows you to define how long the Client Secret for the app will last. It can be 1 year, 2 years, or 3 years.

Client ID and Secret Availability You can choose if you want to make the app available worldwide or in the China market only.

 

Once you have configured the required fields, click the GENERATE CLIENT ID button and you will be prompted with step two of the wizard, which is called Obtain Client Secret. There, you will find a recap of the previously filled fields and the autogenerated Client Secret. Copy them in a safe place because you will not be able to access the Client Secret after you close the current page.

 

The page will also provide you the start and end date of validity for the Client Secret, and you should keep track of them to refresh the Client Secret before its expiration. Once you have created the Client ID and the Client Secret, you can select the Client ID in the SharePoint Add-in registration process, as we saw in previous paragraphs.

 

Publishing Office 365 web applications

An Office 365 application is slightly different from an Office Add-in or a SharePoint Add-in. As you already saw, to register such an application you will have to select the Web App Using Azure AD option at the beginning of the publishing wizard. Moreover, you will be prompted with requests for some information specific to Azure AD.

 

You will still have the general information like the title, the release date, the categories, the logo, and so on. However, the version number is a free text field in which you can write the version number instead of a calculated field that is generated from the manifest file of the add-in.

 

Furthermore, to register an Office 365 application you have to provide some information specific to Azure AD through a section called App Registration, in which you will find the following fields:

 

Azure App ID This is the GUID representing the Client ID of the application, which you registered in Azure AD following the procedure illustrated in blog 4, “Azure Active Directory and security.”

 

Azure Active Directory Instance This allows you to choose if the application has been registered in Azure Active Directory Global, which is the more common case, or in Azure Active Directory China, if you are targeting the Chinese market. There are two more options related to Microsoft Account and Microsoft Account + Azure Active Directory Global. However, they are not yet available at the time of this writing.

 

All of the other steps of the registration procedure are similar to the registration process for an Office Add-in or a SharePoint Add-in. You will go through the Overview, the Details, the Block Access, and the Pricing wizard steps, like you did for Office Add-ins.

 

Updating or deleting add-ins or Office 365 web applications

Whenever you want to update your published applications, you can just come back to the Seller Dashboard. Select the red Continue button under the Office area of the home page, and you will see the list of published add-ins or Office 365 web applications. Select an item to open a page with all the related details.

 

Within that page, you can click the Edit Listing button, or you can click the Edit Draft button if your solution has not yet been submitted for approval and thus is not yet published. You will be able to update both the properties and the whole .APP package or the XML manifest file. You cannot update an add-in or application that is pending approval.

 

Keep in mind that once you update a SharePoint Add-in package or a manifest file, you will have to go through the validation process to see the refreshed solution available in the Office Store. In contrast, from an Office 365 web application perspective, often you will need to update the web application in the hosting environment and will not need to update anything else in the Seller Dashboard.

 

If you want to update the version number or the pricing model according to the rules and restrictions that you have seen or want to update any other property or image for the Office 365 web application, you will have to go to the Seller Dashboard. Nevertheless, as long as you need to update the UI or the business logic of the Office 365 web application, you are not required to update the Seller Dashboard.

 

If you want to delete an add-in or an Office 365 web application, you can follow the same procedure that you use for editing it, but you will have to click the Delete button within the Details page. You can also remove an add-in or an application from the Office Store without deleting it permanently. You just need to click the Unpublish button, which is available only for published solutions. It will take a few days to unpublish the solutions from the Office Store listings.

 

Licensing model

In the previous section, you saw that whenever you sell add-ins or applications through the Office Store you can choose a pricing model, which is related to a specific licensing model.

 

You saw that you can provide your solution for free, but you can also sell it by using a per-seat or per-site model or by using a subscription model. Aside from the marketing and sales perspectives, which are out of the scope of this book, it is important to understand how you can programmatically manage and monitor that your customers use your solutions according to the pricing and licensing model that you choose. You will need to verify and enforce that everybody uses your solutions according to the legal use policies that you defined.

In the following paragraphs, you will dig into the techniques for managing and checking licenses from a developer perspective.

 

Types of licenses

First of all, it’s worth explaining in detail the types of licenses available through the Office Store, based on the flavors of solutions that you can publish. Note that an Outlook Add-in can be purchased on a per-site basis only by an administrator, thereby making it available to all the users of the organization.

 

All the paid or subscription options can have a trial period, if you want. Whenever a customer acquires an add-in, whether it is for free, paid, or with a subscription, the Office Store will require the customer to log in with a valid Microsoft account and will generate a license file on the fly, downloading a license token to the purchaser’s environment.

 

For an Office Add-in (Task Pane or Content), the license token will be stored in the user’s Office client application. For an Outlook Add-in, the license token will be downloaded and stored in the user’s mailbox for a per-user scope or in a system mailbox at the tenant level for a per-site scope. For a SharePoint Add-in, the license token is downloaded and stored in the SharePoint tenant or farm deployment, depending on whether the SharePoint environment is in Office 365 or on-premises.

 

Furthermore, in the case of SharePoint Add-ins, only site, tenant, or farm administrators can install an add-in. Thus, only those kinds of users can purchase an app, and they can assign the purchased licenses to target users for whom they purchased the licenses.

 

Whatever your scenario, a license token has an expiration and must be renewed periodically. The only exceptions are the add-ins with a perpetual license type, which have a token that does not expire. The license token expiration model also supports the trial period for add-ins.

 

When a user launches an Office Add-in (Task Pane or Content), the Office client application checks the license token and renews it if necessary and possible. If the license token is expired and the license model is a trial, the user will not be able to launch the add-in unless she converts the trial into a paid license.

 

When a user launches Outlook and logs in, the Exchange server infrastructure will check for the license token validity of any Outlook Add-in and will renew it if necessary and possible. For SharePoint Add-ins, the license token is verified and renewed by the SharePoint App Management services by using a specific timer job. It is also possible to renew license tokens manually, if needed.

 

Checking license in code

To ensure that only authorized users use your solutions, you will have to enforce license checks within your solutions. License checks require checking a license token file and consuming a service provided by the Office Store. In the following paragraphs, you will learn how to accomplish this task.

 

License XML schema

First, it is important to understand that an Office Store license token is an XML file that adheres to a specific schema. 

 

LISTING Sample outline of the XML license file for an Office Add-in

<r>
<t aid="{2[A-Z] 8-12[0-9]}" pid="{GUID}" | "string" cid="{16[0-H]}" oid="{GUID}" did="{GUID}" ts="Integer" et="Free" |"Trial" | "Paid" sl="true | 1" | "false| 0" ad="UTC" ed="UTC" sd="UTC" te="UTC" test="true | 1" | "false | 0" ss="0" | "1" | "2" | "3" | "4" />
<d>VNNAnf36IrkyUVZlihQJNdUUZl/YFEfJOeldWBtd3IM=</d> </r>

As you can see, the schema is minimal and provides information about the license type, the pricing model, and so on. In Listing, you can see a test XML license file for a SharePoint Add-in licensed for 10 seats with a valid subscription active for one year from the date of purchase.

 

LISTING Sample test XML license token for a SharePoint Add-in

<r>
<t aid="WA900006056" pid="{F49ADBD9-6D1F-4396-B52C-9289ADC6DFF8}" cid="32F3E7FC559F4F49" did="{9EB13155-D449-430B-A1BC-7DBB95573F01}" ts="10" et="Paid" test="true" ad="2016-06-02T09:15:12Z" ed="2017-06-02T09:15:12Z" sd="2016-06-02T09:15:12Z" te="2016-07-02T09:15:12Z" ss="1" />
<d>VNNAnf36IrkyUVZlihQJNdUUZl/YFEfJOeldWBtd3IM=</d> </r>

 

In the code sample of Listing the encrypted token is fake, but it is a test license token, so the encryption token will not be verified by the license server.

 

License check in Office Add-ins

Whenever a user launches an Office Add-in from an Office client application, the home page of the add-in will be loaded through an HTTP request, which will receive in the query string an argument called et that represents the license token to check.

 

The et argument contains the license token formatted as a base-64 string that has been URL-encoded. Thus, to retrieve the real value of the license token you will have to decode it using the base-64 algorithm and then URL-decode the resulting value. As result, you will have an XML license token according to the schema described in the previous paragraphs. If your add-in is an Outlook Add-in, the et query string argument will be URL-encoded only, and you will have to skip the base-64 decoding phase.

 

If the user is consuming your add-in anonymously, the et token will not be provided to the home page of the add-in, so your add-in will have to be ready for this situation if it allows anonymous access. In general, you should always check for the existence of the et query string argument before using it, and you should provide a specific user experience in case the argument is missing. In Listing  you can see a code excerpt about how to decode the et argument to retrieve its XML value.

 

LISTING  Code excerpt to decode the et query string parameter of an XML license token

String etValue = Request.Params["et"].ToString();

Byte[] decodedBytes = Convert.FromBase64String(etValue);

String decodedToken = Encoding.Unicode.GetString(decodedBytes);

 

Be careful; you should never touch or change anything in the resulting decoded token because the encryption token included in the XML license token is valid only if the XML content has not been tampered with. Thus, you should store the decodedToken value without changing or directly processing its content.

 

To validate the XML license token value, the Office Add-in will have to consume the Office Store license verification service, which is available at the following URL: https://verificationservice.officeapps.live.com/ova/verificationagent.svc.

 

The service is available as a SOAP service and through a REST endpoint. By invoking the above URL, you will get back the WSDL (Web Services Description Language) of the SOAP service. For example, you can add a service reference to your .NET web application that provides the Office Add-in implementation, and you can consume the license verification service with a code syntax like.

 

LISTING Code excerpt validating an XML license token through the Office Store license verification service

VerificationServiceClient svc = new VerificationServiceClient();
var result = svc.VerifyEntitlementToken(
new VerifyEntitlementTokenRequest() { EntitlementToken = decodedToken });
if (!result.IsValid) {
// Handle any license issue here
}

 

As you can see, there is a VerifyEntitlementToken operation, which is the only one provided by the service, and which verifies the XML license token. The operation returns an object of type VerifyEntitlementTokenResponse, which provides information about the verified license. The most important property of the response object is the IsValid Boolean property. If the IsValid property has a value of true, the license is valid; otherwise, it is not.

 

Another interesting property of the response object is the IsTest Boolean property, which enables you to check if the current license is for testing purposes only. In general, there are a lot of useful properties that you can use—for example, to enable specific features if the license is a paid one. 

 

You can play with these properties in your code and implement complex behaviors based on the license status. In Listing, you can see a full sample about how to handle the main statuses of a license.

 

LISTING Full code sample about how to handle the license verification response

if (result == null ||
result.ProductId != TARGET_PRODUCTID ||
!result.IsValid) {
// The license is not valid => redirect user to an "UNLICENSED" message/UI
}
else if (result.IsValid) {
switch (result.EntitlementType) {
case "Free":
The license is valid and Free => enable Free features break;
case "Paid":
// The license is valid and Paid => enable Paid features
break;
case "Trial":
// The license is valid but Trial => check Trial period
if (result.EntitlementExpiryDate < DateTime.Now || result.IsExpired)
{
The Trial period is expired => redirect user to
a "TRIAL EXPIRED" message/UI
}
else {
// The Trial period is valid => enable Trial features, only
}
break;
}
}
#if DEBUG
else if (result.IsTest) {
// The license is for testing purposes only => behave accordingly
}
#endif
else {
// The license is not valid => redirect user to an "UNLICENSED" message/UI
}

 

Notice the check against the test license, which is wrapped in the conditional compilation for a DEBUG compilation. Thus, if there is a released version of your code, you will reject any testing license. As explained earlier, the license verification service is also available as a REST endpoint, which can be invoked through an HTTP GET request against the following URL: https://verificationservice.officeapps.live.com/ova/verificationagent.svc/rest/verify?token= {decodedToken}

 

In the previous URL, the token query string argument represents the XML license token encoded by using, for example, the Uri.EscapeDataString method of .NET or the encodeURIComponent() method of JavaScript.

 

Note

If you are developing an Office Add-in by using Microsoft Visual Studio, you can add an XML license token file to the OfficeAppManifests subfolder of the bin\debug or bin\release of the project to have a behavior close to the one you have when executing the add-in from the Office client environment through the Office Store. The license token file name has to be the same as the manifest file, but with a .tok file extension.

 

License check in SharePoint Add-ins

Much of the information shared in the previous section about how to check licenses in an Office Add-in also applies to validating a SharePoint Add-in license. However, in a SharePoint Add-in, the license is not provided through a query string parameter, and you have to query for any available license by using the client-side object model (CSOM).

 

In Listing  you can see a code excerpt about how to retrieve licenses for a currently running SharePoint Add-in. The code excerpt also leverages the OfficeDev PnP Core Library extensions, “Overview of Office 365 development,” to improve the overall availability of the code.

 

LISTING Code excerpt to retrieve an XML license token for a SharePoint Add-in

VerifyEntitlementTokenResponse result = null; VerificationServiceClient svc = new VerificationServiceClient();
AuthenticationManager authManager = new AuthenticationManager();
using (ClientContext context = authManager
.GetSharePointOnlineAuthenticatedContextTenant( targetSiteUrl, userName, password)) {
var licenses = Utility.GetAppLicenseInformation(context, TARGET_PRODUCT_ID);
context.ExecuteQueryRetry();
foreach (AppLicense license in licenses.Value) { var xmlToken = license.RawXMLLicenseToken;
result = svc.VerifyEntitlementToken(
new VerifyEntitlementTokenRequest() { EntitlementToken = xmlToken
});
if (result == null ||
result.ProductId != TARGET_PRODUCTID ||
!result.IsValid) {
// The license is not valid => redirect user to an "UNLICENSED"
message/UI
}
else if (result.IsValid) {
switch (result.EntitlementType) {
case "Free":
The license is valid and Free => enable Free features break;
case "Paid":
The license is valid and Paid => enable Paid features break;
case "Trial":
The license is valid but Trial => check Trial period if (result.EntitlementExpiryDate < DateTime.Now ||
result.IsExpired) {
The Trial period is expired => redirect user
to a "TRIAL EXPIRED" message/UI
}
else {
// The Trial period is valid => enable Trial features,
only
}
break;
}
}
#if DEBUG
else if (result.IsTest) {
// The license is for testing purposes only => behave accordingly
}
#endif
else {
// The license is not valid => redirect user to an "UNLICENSED"
message/UI
}
}
}

The GetAppLicenseInformation method of the Utility class, which is defined in the Microsoft.SharePoint.Client.Utilities namespace of CSOM, allows you to retrieve all the currently active licenses for the current user and the current SharePoint Add-in. The result is a collection of licenses that usually is made of a single item but could be a collection of items.

 

Thus, you should walk through all of them to seek any useful information. For example, you may have to merge the information of multiple licenses to determine the real licensed features for the current user related to the current product. The XML license token is inside the property RawXMLLicenseToken of each item of the collection, which is of type AppLicense.

 

In the code sample illustrated in Listing, we iterate through all of the licenses and validate each of them. In a real solution, you probably should do something more sophisticated.

 

Aside from that, the verification process is almost the same as for an Office Add-in license. The Office Store license verification service does the license verification process, and the response is exactly the one you saw in the previous paragraphs. For testing purposes, you can load up to 10 test licenses into your environment by using the ImportAppLicense method of the Utility class.

 

See Also

You can find further details about the ImportAppLicense method in the document “Utility.ImportAppLicense method,” which is available at the following URL:  https://msdn.microsoft.com/en- us/library/office/microsoft.sharepoint.client.utilities.utility.importapplicense.aspx.

 

Best practices for handling licenses in code

In the previous paragraphs, you learned how to handle licenses and how to check XML license tokens in code. However, it is better to understand when and how to apply license checks than to continuously execute verifications for every request.

 

Invoking the Office Store license verification service introduces latency in your application logic, and you should avoid checking a license upon every request. It is better to verify the license once, when the user launches your application, and cache the response you get back from the service into a state variable that you can query later if necessary.

 

For example, you could store the needed information—like the license expiration (if any), the entitlement type, and so on—within a persistent state variable and query that state variable whenever you need to enable a feature or capability that requires a proper license to work.

 

The state persistence layer can be a trivial session object if few users use the application and you don’t need to provide high availability for your solution. However, if you need to run the application on multiple servers or service instances, whether for high availability purposes or for scalability goals, you should rely on a scalable service like the Azure Redis Cache. You can also consider using a session provider that relies on the Azure Redis Cache to keep the implementation simple but still highly available and scalable.

 

Note

You can find further details about Azure Redis Cache by reading the document available at the following URL: Azure Redis Cache | Microsoft Azure. Moreover, you need to consider that the license checks can be done on the server side only. Thus, you cannot plan to perform license checks for an Office Add-in within the JavaScript client code that runs inside the Office client application.

 

If your application is a SharePoint Add-in, you can run the license checks in the main controller of  ASP.NET web application if the add-in is provider hosted and runs in ASP.NET MVC. Moreover, if the SharePoint Add-in is a SharePoint-hosted add-in, you should rely on an external server to perform the license checks. Any client-side JavaScript license check code could be tampered with by a malicious user.

 

For example, in the main page of the add-in or in any of the app parts, you can embed an image that loads from an external service and that checks the license on the server side. In case of any failed license validation, you can show an alert image instead of the logo of your add-in. Moreover, you can consider providing a custom REST API that you invoke from the SharePoint-hosted add-in instead of using an embedded image, but you need to be careful because a JavaScript request against a REST service can be tampered with.

 

It is all about how critical it is to protect your SharePoint Add-in. In general, if you want to protect your solution with a strong licensing model, having a provider-hosted solution instead of a SharePoint-hosted one is the better option. A provider-hosted solution is also better from an architectural and scalability perspective. Thus, any real enterprise-level solution should be implemented as a provider-hosted add-in, not only for license checks but also from an architectural perspective.

 

Metrics and company profile

Another interesting option you have once you have published an add-in or a web application is to monitor the results in terms of sales and usage of your solution.

 

Metrics

Within the Seller Dashboard, you can click the Metrics tab after selecting the red Continue button under the Office area, and you will be able to monitor some useful numbers about your solutions. First of all, you have to select a published add-in or application for which you want to see the metrics.

 

The metrics available through the Seller Dashboard are related to the latest four weeks and include the following insights:

  • Browser hits The number of times your solution has been viewed in the Office Store
  • Downloads The number of times your solution has been downloaded from the Office Store
  • Trial downloads The number of times your solution has been purchased for free from the Office Store
  • Trial conversions The number of times a trial has been converted into a paid version of your solution
  • Purchased seats The overall number of seats that have been purchased for your solution
  • Purchased site licenses The overall number of site licenses that have been purchased for your solution
  • For SharePoint Add-ins only, you will also have the following metrics related to the time frame of analysis:
  • Installs The overall number of install attempts
  • Launches The overall number of times the solution has been started
  • Daily unique users The sum of daily unique users for your solution
  • Uninstalls The overall number of uninstall attempts
  • Failed installs The overall number of failed installs, including any retries
  • Runtime errors The overall number of errors logged by SharePoint and by the solution within its custom code
  • Failed upgrades The overall number of failed upgrades, including any retries
  • You can also see reports in terms of sales by following the link named View Sales And Tax Data, which will open a specific sales report.

 

Office Profile

Another set of information that you can manage during the lifetime of your projects is the Office Profile, which can be managed by clicking the Office Profile tab.

 

Through that page, you will have the capability to update the following data about your company:

  • Logo of the company The logo that will be shown in the Office Store for your company
  • Description The description of your business, useful in the Office Store
  • Website The URL of the website for your business
  • Marketing Contact Email The email address of a marketing contact in your company, if any
  • Address The physical postal address of your company Phone Number The phone number of your company
  • The Seller Dashboard will use all of these fields to build you company profile in the Office Store.

 

Summary

In this blog, you learned how to publish your SharePoint Add-ins, Office Add-ins, and Office 365 web applications, whether you want to target the Corporate Catalog or the public Office Store. You saw that all of these solutions share the same publishing tool, which is called the Seller Dashboard, if you want to sell them worldwide.

You learned how to create, submit for approval, and publish a new solution and how to update or delete a published one. Furthermore, you saw how to monitor your metrics and sales. Moreover, you understood the pricing models available through the Office Store and how you can control licenses and subscriptions within your custom developed solutions, working with license files and the Office Store license verification service.

You are now ready to create your real business solutions and eventually to sell them in the Office Store or publish them in the Corporate Catalog. Have fun

Recommend