Testing a Web Add-in with Multiple Manifest

First, let’s discuss the architecture and from an Outlook/Exchange perspective:

  1. First, you load your manifest on the Exchange server. The manifest is simply an XML file that contains pointers to your web site (on the IIS server).
  2. When you load Outlook Web Access and click the add-ins button, the Add-ins pane will appear and each application manifest you have loaded will appear in the list.
  3. When you click on one of  the add-is, the task pane will load (in Orange) and your site (located on the IIS Server) will populate in the pane.

The problem arises when you have but ONE Exchange server or ONE developer account for development and test. Developers want to be able to debug the code they are working with which typically loads  from the local instance of IIS Express (localhost). But testers need to be able to work with the latest release build to test.

So, how do you do this?

The key is in the Manifest file. If you open the Manifest file, you will see the <Id> field. This is the most important piece, but there are other areas you should/need to update as well. What you will essentially have is two copies of your manifest file:

The first file will use the default ID provided in the project, it should have a name like Developer Release and it will point to your localhost (this is default with the setup of a new Web Add-in).

<?xml version="1.0" encoding="UTF-8"?>
...
<Id>aaaaaaaa-3c56-4091-951e-18fda9e471d4</Id>
<Version>1.0.0.2</Version>
<ProviderName>David E. Craig</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Demo Developer Release" />
...
<FormSettings>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://localhost:44303/AppCompose/Home/Home.html"/>
</DesktopSettings>
</Form>
</FormSettings>
...
</OfficeApp>

 

diagram - developer - manifest

The second file will have a different unique ID, a different name (like Tester Release) and it will point to your IIS website.

<?xml version="1.0" encoding="UTF-8"?>
...
<Id>bbbbbbbb-3c56-4091-951e-18fda9e471d4</Id>
<Version>1.0.0.2</Version>
<ProviderName>David E. Craig</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Demo Tester Release" />
...
<FormSettings>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://TestServer/AppCompose/Home/Home.html"/>
</DesktopSettings>
</Form>
</FormSettings>
...
</OfficeApp>

 

diagram - tester - manifest

The real key is having different ID’s and publishing them separately. In Visual Studio the developers will have everything set as shown above (essentially leave everything as default). When they are ready to drop a build to the testers:

  1. Right-click on the Website project and click Publish. Follow the steps to publish the site to the Testers server.
  2. Right-click on the Manifest project and click Publish. Click the “Package the add-in” button.
  3. In the resulting dialog, enter the URL to the Testers site, This will ONLY update the URL, but not change the ID.
  4. Open the <manifest>.xml file in Notepad and then change the fields as shown above:
    • Modify the ID
    • Change the name so that you can identify which one is which
    • Verify the URL is correct.

At this point you are ready to go. And you can create MULTIPLE versions of your manifest. If for example you need one for Testers, one for Developer and then another for Pilot and yet another for experimental testing (each pointing to different IIS instances, sites or even to the Cloud (Azure). You can create as many manifests as you need this way, have them all show up in the Add-ins task pane allowing the testers/users to select the one they wish to work with.

Useful Refresh Command

I recently watched a video by my colleague Michael Zlatkovsky in which he demonstrates the changes to the OfficeJS Libraries. And in this video he proposed a nifty little trick I have been using ever since to refresh the task pane app without having to stop and reload the solution (which saves minutes each time you need to stop debugging). What you do is place a refresh button at the bottom of your task pane HTML, like this:

<id="refresh-button">Refresh</button>

Then you wire it up like this:

$('#refresh-button').click(function () {
    location.reload();
});

Simple eh?! Thanks Michael for the nifty tip! wlEmoticon-hotsmile.png

Simple Web Service Controller for Office Add-in

I have found Web Service Controllers in Office Add-ins to be quite useful. There are a number of reasons you might want to keep functions of your add-in on a server, including obfuscation, complex calculation, data intense, service mashups, and much more. However, every time you want to use your Web Ser4vice from the JavaScript interface, there is a lot of code associated with the AJAX call you have to make. This begs for simplicity and that is what I have done. In my App.js project, I added a simple makeAjaxCall function that takes the command you want to invoke, the parameters you want to pass it (as an array) a callback when the call is complete and a callback if there was an error. Here is the core code for the makeAjaxCall() method:

// Helper function to call the web service controller
app.makeAjaxCall = function (command, params, callback, error) {
	var dataToPassToService = {
		Command: command,
		Params: params
	};
	$.ajax({
		url: '../../api/WebService',
		type: 'POST',
		data: JSON.stringify(dataToPassToService),
		contentType: 'application/json;charset=utf-8'
	}).done(function (data) {
		callback(data);
	}).fail(function (status) {
		error(status);
	})
};

Here is a sample of my web service controller:

/// <summary>
/// CORE SERVICE FUNCTION
/// This function will take a web request which will contain the command the caller wants
/// to initate and then the paramaters needed for that call. We enter a SELECT statement
/// below to determine the command given and we then call the proper helper function
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost()]
public WebServiceResponse WebService(WebServiceRequest request)
{
	WebServiceResponse response = null;
	switch(request.Command)
	{
		case "DoFunctionA":
			return functionA(request.Params);
		case "DoFunctionB":
			return functionB(request.Params);
		case "DoFunctionC":
			return functionC(request.Params);
		case "DoFunctionD":
			return functionD(request.Params);
	}

	response = new WebServiceResponse();
	response.Message = "Unknown command";
	return response;
}

And here is how you call it:

app.makeAjaxCall("DoFunctionA", ["Value1", "Value2"], function (data) {
var result = $.parseJSON(data.Message);
// do something with the retuirned result here...
});

 

For more information on how to create a Web Service controller, there is a great blog post from a colleague of mine, Michael Zlatkovsky on how to do this:

Create a web service for an app for Office using the ASP.NET Web API

 

 

[UPDATE] In which Application is my Add-in running?

UPDATE: The code outlined in this post will no longer work with Office Online. See this post for more information: http://stackoverflow.com/a/32851938/678505. Specifically, this block:

Update Dec 5, 2016: We will soon be releasing an API to detect the platform info (partially in response to the fact that the _host_info URL paramater, which folks had unofficially relied on, needed to be recently removed for Office Online). We also have a temporary workaround in anticipation of the forthcoming official API. See “In Excel Online, OfficeJS API is not passing the host_Info_ parameter anymore to Excel Add-In” for information on the API and workaround.

Original post:

In case you missed the news, there was an update to the Office API’s. Version 1.2 was announced at //build and released 2 weeks ago. This API enhancement gets us closer to parity with the native object models used in VBA and VSTO. One interesting aspect of the new Office add-ins (formally known as Apps for Office) is how you can use the same codebase across all the applications. However, as the API becomes richer they are also becoming more product specific. Meaning, there is now the Excel.run() and Word.run() commands (PowerPoint is a little lagging in this area, but it is supposedly on the horizon).  These allow you to do Excel Workbook (or Word document) context specific commands. And there is a lot of other goodies (like promises). But, I digress… wlEmoticon-hotsmile.png

I was working on a proof of concept for my customer when I found that I needed to use the same codebase for a lot of the same work, but in some cases I needed to do something specific in Word and in another, something specific in Excel. I came up with the following function that I placed in my App.js file:

var current;
app.hostTypes = { Word :"Word", PowerPoint :"PowerPoint", Excel :"Excel"};
app.getHost = function () {
	if (current == null) {
		if (Office.context.requirements.isSetSupported('WordApi')) {
			current = app.hostTypes.Word;
		} else if (Office.context.requirements.isSetSupported('ExcelApi')) {
			current = app.hostTypes.Excel;
		} else {
			var host = $.urlParam("_host_Info");
			if (host.toLowerCase().indexOf("word",0) &gt;= 0) {
				current = app.hostTypes.Word;
			} else if (host.toLowerCase().indexOf("excel",0) &gt;= 0) {
				current = app.hostTypes.Excel;
			} else {
				current = app.hostTypes.PowerPoint;
			}
		}
		return current;
	}
	else {
		return current;
	}
};

To use this you would do this for Word:

            /// WORD
            if (app.getHost() == app.hostTypes.Word) {
                // Word specific code where
            }

Or, this for Excel:

            /// EXCEL
            if (app.getHost() == app.hostTypes.Excel) {
                // Excel specific code here
            }

Debugging with Office Online

Recently I began working on an Office Add-in (formally known as Apps for Office) that interfaces with Excel, PowerPoint and Word online. By default, when you create a new Office Add-in in Visual Studio 2015, it will default to using the installed Office Desktop Client. I have done a lot of work with Mail Apps, but this was the first time I really delved deeply into Office Add-ins with the express need to design against the online versions. After pulling my hair out looking for the settings in the debug tab of the Manifest Project settings, I found this in the Properties page of the Manifest Project file:

When you select the project at the top, you see these properties in the Properties window (F4).

Simply change these as such:

You then specify the URL to your Office 365 subscription. To get this, I logged into my Office 365 developer account from http://office365.com selected my OneDrive folder from the menu and the copied the full path from the address bar and placed it in the field. I got a prompt to log in and then all was good.

The next problem I had was with side loading the application. When I tried to Debug, I got this error in Visual Studio:

Error occurred in deployment step ‘Install app for SharePoint’

According to the documentation and everything I can find, you should not get this error if you are using an Office 365 Developer account. I am, and I am still getting this error. So, I had to go into my account and enable Side loading. I searched and searcha nd then found this blog by Tobias Lekman: https://blog.lekman.com/2012/11/sharepoint-2013-sideloading-of-apps-is.html. He gives two simple enough sounding steps:

  1. Download and install the SharePoint Online Management Shell for PowerShell
  2. Download the script Sideload.ps1 and execute it within the SharePoint Online Management Shell.

Sounds easy. So, I downloaded and installed this: https://www.microsoft.com/en-us/download/details.aspx?id=30359.

Then, I downloaded script and placed it right in my PowerShell folder: http://lekman.codeplex.com/releases/view/98505.

Now, I am the first to admit, I am a developer and not a PowerShell scripter. So I have next to no experience with PowerShell. When I ran the script I got an error:

Essentially there is an execution policy preventing my PowerShell script from running. So, I went to the link provided: https://technet.microsoft.com/library/hh847748.aspx. From there I found I needed to run this command:

After, I did that I was able to run the Sideload.ps file, it asked for my url, username and password and then setup my side to allow the side load for testing.

When I returned to Visual Studio and clicked Run, it ran and installed my app.