I have been working on improving my various Outlook proof-of-concepts and nearly every one of them require using EWS to access data about items in Exchange, which are not exposed through the object model. As such I have been constantly improving easyEWS.js. I have just posted version 1.0.6 and also have provided a published CDN which you can now use to always get the latest version of the library. Here is the CDN:
Typed every var using JSDoc. See my post on that here. This should help with debugging any issues you have. And if you have issues with my library, please contact me.
Updated the Error handling callbacks and the Debugging callback to include more information. Every function can be called with this pattern, and you will get more information now if something does wrong. Here is the essential pattern every function uses:
[code lang=”javascript” collapse=”false”]
easyEws.fn([x, [y],] function(z) {
// this is the success
}, function(error) {
// this is the failure – error
}, function(debug) {
// this is the debug information which contains:
// the soap message sent to the server
// the soap response from the server
// the server status message
// the server error message if any
}
Ok, odd title, I know. For some developers “in the know,” they will understand what I am eluding to. So what does it mean? It is short for Yeoman. In their own words, here is what it does:
Yeoman helps you to kickstart new projects, prescribing best practices and tools to help you stay productive. To do so, we provide a generator ecosystem. A generator is basically a plugin that can be run with the `yo` command to scaffold complete projects or useful parts.
So, by “yo office”, I am telling you that Office Web Add-in templates can be delivered through this cross-platform command line tool. This means you do NOT need Visual Studio (or even Windows) to create a new Office Web Add-in project package. More importantly, these packages are setup through NodeJs Package Manager and are configured to use Node.js web server (which comes on MANY different platforms).
Now, if you are like me (a 20+ year VS Office Developer veteran), when I first heard this, it all sounds just like this:
Γράφω στα ελληνικά και αν καταλαβαίνετε αυτό, καλό για σας
FYI: That is Greek.
So, I have a choice here and that is to make this a long blog post, or a short blog post and I dislike making long posts as much as you do reading them, so… In this case, here is the executive summary: The Microsoft Office Developer team is trying to make Office an accessible development platform no matter where you come from (experience wise) or what platform you use.
Now, for “super hip” developers living on the cusp of command-line based free developer tooling, put this in your MacBook:
yo office
For the rest of you…
To get this to work, you need to install Node JS from here. And next you need to install the prerequisites as per these steps. Once installed, you will open the Node JS command prompt (in Windows, from the Start menu, type NodeJS and it should appear as a choice) and perform these steps:
Change directory (using cd) to the folder where you want the project to live.
Type “yo office” and then answer the questions.
In my case, I developed an Outlook add-in, with a manifest, using JavaScript, called “Outlook-Sample-1” and I chose JQuery as the framework. Here is the output:
When it is done, it will create a folder called Outlook-Sample-1 and place a set of files in it that you can edit and the NodeJS configuration to run it from a standalone web server locally. I used Visual Studio Code (or VS Code, as it is known, which is a new multi-platform code editor from Microsoft that runs on Mac, Windows, and Linux), to edit the add-in.
NOTE: If you have configured everything in Visual Studio Code correctly, you can not only get NodeJS to interact from the “Terminal Window” you can also hook to GitHub to push/pull your projects. It is a whole other topic for a whole other blog post, but VS Code is a bare essentials utilitarian cross-platform code editing development tool.
Now, which files you need to edit and such are beyond the scope here, but if you have done some development in this area before, you can probably quickly figure it out. I might post more on this in the future. Stay tuned!
Ok, ok… I know, I know… I need to stop here and address the big huge hulking elephant in the room. YES, these are command line tools. Yes, DOS is still dead. No, it is. Really. Repeat after me, DOS “IS” DEAD. Now, just accept that this is the way it is, because this is the way it is. I know, it seems so… 1994… Neo called from the Matrix to assure you it is, indeed, 2017. For my fellow Visual Studio, 20+ year developer gurus: “deep breaths.” Now, back to our regularly scheduled broadcast..
Here is a view of my project from VS Code, with the Terminal Window open…
Once I was done, I opened a NodeJS command prompt in the Terminal window and launched my project using the following command:
npm start
Once you run this command, you will effectively start NodeJS web server at http://localhost:3000 which will be hosting your WebAdd-in. But you still need to side load your manifest. To do this, you will need to follow these steps (for Windows), here for Mac, here for Outlook. This gets all kinds of technical and deep into other areas that are potentially future blog posts, but the point is that you can then debug your add-in locally no matter which platform you are on.
Ok, Ok… back to the elephant…
So, is this BETTER than Visual Studio Enterprise and the JavaScript debugger there and code assistance I get?
Well, this is more nuanced… I lean towards “no” as most of this stuff is too lightweight, especially given my experience. Especially without the debugging. But, I do see some advantages (besides the command line tools) that have its advantages for use on Windows:
It is available across multiple platforms – including Windows. VS Code, Node JS, and “yo office” are everywhere, anywhere, over there, under that, etc..
Out-of-the-box support for TypeScript. Which I have not blogged on a lot, but Michael Zlatkovsky has covered well in his book. TypeScript makes JSDoc seem like child’s play.
Out-of-the-box support for the Node Package Manager ecosystem. This is a huge repository of reusable code.
VS Code is a light weight editor that hooks into NPM, Node and Git really well. And while it does not debug, recent builds of Office have added the ability to hook to a debugger right from your taskpane. Which is only good if you have a taskpane, but it is a start.
Node provides auto-refresh (browser-sync), auto-compilation, and other goodness.
Again, this might look like a lot of Greek, but if you find your shop turning Greek, you can at least know some starter phrases to begin acquiring the knowledge you need to speak it.
So, why is any of this important to me? Especially if you are telling me:
“I use Visual Studio Enterprise (or Pro) and I am happy with it, why would you introduce me to this Greek language lesson.”
The answer comes in three points you can take away:
Office development is really multi-platform capable now. This correlates to the fact the Office Applications are also multi-platform. Write your add-in once, and it will run (or the future plan is, it will run) on every platform you can think of: Windows, Mac, iOS, Android, Linux (or essentially an browser or any platform with a browser that has HTML5 support).
You know more about the vision, commitments and goals of the core Microsoft Office teams. If you get nothing out of this other than Microsoft is committed to true multi-platform capabilities with Office Web Add-ins, you have come away with a major point. But there is also the knowledge on how Office development can be done no matter the developer background. And hey. You 20+ year VS Office Developer veterans: take this to heart and read into it, because these are the tea leaves; then look over to your mentee that is likely your kids age and <sigh>.
You can move to another platform and still take what you know with you. This means that “cool” MacBook you bought several years back and gave to your kiddo when they went off to college can actually do something more than read email and browse web pages. Uh, I still strongly suggest you stick to Web Add-in development on Windows and Visual Studio Enterprise if you are already there… just saying…
So, with this, I hope I have sufficiently given you a vision of things to come, and maybe even given a few of you some new things to research and delve into. If you want more, the entire how-to with Yeoman, including a cool video, is on the following page:
If you are an Outlook Web Access developer trying to create an add-in to help mitigate the accidental release of Personally Identifiable Information (PII), you have probably been looking for a way to stop the send of the message if it does not pass a test. The functionality has not been a part of the new OfficeJS object model, until now. However, there are several things you need to know in order to set this up. Here are the general steps:
Use PowerShell to create a policy to enable to the OWAOnSendAddinAllUserPolicy
Use PowerShell to set that policy for a mailbox or group of mailboxes
Update your manifest file to enable the functionality and specify the event function you want to call when the user presses send
Write your JavaScript to properly handle the event.
NOTE: There are several caveats to this functionality. These are: you CANNOT publish an app using this functionality to the Store; this will not work on Shared mailboxes; and this will not work from any other clients, just Outlook Web Access (for now).
Create the OWAOnSendAddinAllUserPolicy
To set the policy for a user account, you need to create a policy with it enabled (or edit the default or specific policy you have applied). First, you need these steps to connect to the Exchange server as an administrator and establish a session:
Once you have done, this you can then start writing the functionality into your code.
Update the Manifest file
The manifest will require a new and additional version overrides node. You will then use the FunctionFile to define an Extension point in your code. This will be a function you call. If you are not familiar with Function Files, here is a link to how to create one:
[code lang=”xml” collapse=”true” title=”click to expand if the embedding below is not visible.”]
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
<!– On Send requires VersionOverridesV1_1 –>
<!– https://dev.office.com/docs/add-ins/outlook/outlook-on-send-addins –>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.5">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!– Location of the Functions that UI-less buttons can trigger (ExecuteFunction Actions). –>
<FunctionFile resid="functionFile" />
<ExtensionPoint xsi:type="Events">
<Event Type="ItemSend" FunctionExecution="synchronous" FunctionName="onSendEvent" />
</ExtensionPoint>
…
</DesktopFormFactor>
</Host>
</Hosts>
…
</VersionOverrides>
</VersionOverrides>
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The important thing to note here is that your FunctionFile function for OnSend will need to be defined OUTSIDE the scope of the Office.initialize() function. It needs to be GLOBAL. So you simply place it in the root of your functionFile.js. However, you still need to define an Office.initialize() function in your FunctionFile and in there you probably will want to grab a reference to the mailbox, mailbox.item and the user maybe and place them in a global var as well. This way you will have access to those things in the Event method to be able to make call like body.getAsync() or subject.getAsync(), etc.
/// <param name="event" type="object">ItemSend event is automatically passed by on send code to the function
// specified in the manifest.</param>
sendEvent = event;
// do your work here… since it will likely be ASYNC, you will want to
// set the event handler to a global. When you are done, you will issue
// > sendEvent.completed({ allowEvent: true }); // to send
// > sendEvent.completed({ allowEvent: false }); // to block
doSomething.Async(values, asyncComplete);
}
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Then later in your code, you can either Cancel the send or allow the send event to occur, like this:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
function asyncCompelte() {
// procress the result
if(isOkToSend() == true) {
sendEvent.completed({ allowEvent: true }); // send it
} else {
sendEvent.completed({ allowEvent: false }); // block it
}
}
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Once you have set the policy on the mailbox, modified your manifest and then added the event function to your FunctionFile, you are good to go. I will be working on a simple demo to place on GitHub in coming weeks and will post an update when complete.
Recently, I was asked what is needed in order to create the most basic add-in you can. The least amount of files, work and effort. Technically, you only need two files:
An HTML file with all your JavaScript inline and all the Office and JQuery libraries being pulled from a CDN.
An XML Manifest file that defines the add-in.
You publish the HTML file to a web server, get the address to where it is published, update the manifest with that information and then install the XML file as an add-in. The XML file will define the add-in name/description and points to the HTML file for the code. When you launch the add-in, the task pane will open and the HTML page will be loaded in it. From there your JavaScript will execute and your add-in will come alive. It is THAT easy. 2 files.
To demonstrate this, I created a simple Excel add-in. Here is the code for the bare minimum manifest XML:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Here is the HTML file that is quite basic, I am not even using the Microsoft Fabric for the controls. This is a simple HTML page, period:
[code lang=”xml” collapse=”true” title=”click to expand if the github.com embedding below is not visible.”]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title>Excel Basic Add-In</title>
<!–using JQuery CDN so we do not need to include–>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" type="text/javascript"></script>
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<script type="text/javascript">
(function () {
"use strict";
// The initialize function must be run each time a new page is loaded.
Office.initialize = function (reason) {
$(document).ready(function () {
// Add a click event handler for the button.
$(‘#simple-button’).click(function () {
Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
$("#banner-text").text(‘The selected text is: "’ + result.value + ‘"’);
$("#banner").show();
} else {
$("#banner-text").text(‘Error: ‘ + result.error.message);
$("#banner").show();
}
});
});
$("#banner-close").click(function () { $("#banner").hide(); });
$("#banner").hide();
});
}
})();
</script>
</head>
<body>
<div id="content-main">
<div class="padding">
<h1>Basic Addin</h1>
<div>Select a cell with text and then…</div>
<button id="simple-button">Click Here!</button></div>
</div>
<div id="banner" style="position:absolute;bottom: 0;">
<div id="banner-text"></div>
<button id="banner-close"> <i>X</i> </button></div>
</body>
</html>
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
What this add-in does is very simple. It opens the task pane with a single button. Enter some text in a cell in Excel, select that cell, and then click the button. It gets the text and displays it in a popup at the bottom of the pane. I have published this add-in manifest here:
Open Excel Online. I first log into https://outlook.office365.com. Then I click on the menu button in the upper left and select Excel.
I select to create a “New blank workbook.”
On the Insert tab, I click Add-ins.
In the upper right, I select “Upload My Add-in”
I click Browse and in the Filename box, I put https://basicaddin.azurewebsites.net/basicaddin.xml and then click Open. This will download the add-in to the cache on your system and you will get a file name like “basicaddin[1].xml.”
Click Upload.
The add-in will load from my Azure site and you will see the button demonstrated above.
Now, to go ahead and answer a question I know are coming. How did I upload this Azure without Visual Studio publishing tools:
I went to my Azure Protal (portal.azure.com).
I clicked the +New button on the left side.
I selected Web + Mobile, then Web App
I filled in the required information: Name and resource group and clicked Create.
Then I used the steps above to load it into Excel Online.
This is fairly simple from a web developer perspective. Now, if you are not a web developer (but a traditional Office developer), you are probably still on the fence on how/why you would want to go through all of this. Again, the primary thing I am demonstrating here is how you can build an add-in with as few files as possible. But, I want to reiterate how these new add-ins are truly cross-platform. Follow these steps on your iPad and you will know what I mean. It is incredible and this work really is not that difficult to perform. It might just be a tad time consuming in the beginning as you are getting used to the new interfaces.
If you have questions, please feel free to reach out to me.
I have been working with Outlook Web Add-ins and Compose Message integration for Office Outlook Web Access for a while now. That is, Outlook add-ins that present themselves when the user is composing an email. This scenario is not seemingly as common. But one of my customers has been completely gung-ho about getting all of their COM add-ins converted to the new Office.js model. Some of their solutions will be able to make it there, and some will not. We are finding some roadblocks with mail compose apps.
One area that is currently failing is setting X-headers on an email item. I developed a JavaScript library to assist with this: easyEws. I blogged about it here. The one specific EWS SOAP call my customer wants to use from that library is this one:
updateEwsHeader: Updated the named x-header in the message.
This function works great in two conditions:
Outlook Web Access (or Office 365 Outlook Online), and
Outlook client – Online mode only
If you use Outlook client (Outlook 2016) in cached mode, this call will seem to work (because it does), but the x-headers will be missing when the user presses “Send.” The problem is that while the currently composed item is saved to the drafts folder and the custom x-headers are written to it via the EWS call (server side), the current Outlook Inspector/session is NOT updated. The cached Drafts folder may take (depending on network latency) anywhere from 1 to 3 minutes to be updated, but regardless the item in the current inspector session will NOT ever be updated. So when the user presses SEND, the item in the drafts folder will be overwritten with one that does not contain the custom x-headers and then sent.
There is no way around this limitation. And I have confirmed this with the Exchange and Office.js teams that this is a limitation of cached mode in Outlook. If you have a need for custom x-headers with an Outlook add-in your only option for now is to create (or keep your existing) COM/VSTO add-in.
If you are running into this limitation, please let me know by commenting below.
You know, sometimes the way you want to do things is not necessarily the right way. And even then the right way makes life so hard you want to cry. With Office.JS I recently blogged about “a way” you can test multiple sprints of your add-in in your development environment: Testing a Web Add-in with Multiple Manifest. In this article I explain a method of creating multiple manifests to point to each “version or sprint” of your add-in. Recently, I found that one of my customers threw a wrinkle into that idea. Not that creating multiple manifests was impossible, but they were needing two conflating factors:
Each sprint points to a different folder of HTML/JS.
And they needed to use query string parameters (like this post) to load one of maybe 20 different configuration settings.
With up to 20 configurations per sprint and lets say 4 sprints, you end up with 80 manifest files. So, this is untenable. You need more choices, then enter necessity…
So, lets say you need to test multiple configurations and multiple sprints. Here is how you will set this up:
You will publish each sprint of your Web Site Project into a different folder in IIS. Like this:
Different sprints in separate folders
Then, you will create a new HTML page. I called mine Launcher.html. This page will contain a submit button and two drop-down select controls:
One for sprints
One for configs
Next, you will modify your manifest. You can add a new button called “Sprint Testing” for example and have it launch: Launcher.html.
In the Launcher.JS, populate those dropdowns. You can populate these hard coded into the HTML, from your client side JS or call your service controller. In my example, I call my service controller.
When the user clicks the submit button after choosing the desired settings, you will build the URL to the proper sprint folder and then append the config values to the current query string (that last item is critical so that your sprint Compose/Read page will get the important Office related query string values passed to it). You will see I do this below by replacing my “launcher.html”, replacing it will the new path, then append the config setting:
Once done your code will perform a location.replace() on the url and you will have your proper sprint and configuration loaded. You will only have a single manifest file and you will be able to test/regress test as much as needed with different sprints and configurations.
Here is what the HTML page I created looks like when loaded:
Here is the HTML:
[code lang=”javascript” collapse=”true” title=”click to expand if the github.com embedding below is not visible.”]
<!DOCTYPE html>
<html>
<head>
<title>Select Which Sprint to Launch</title>
<meta charset="utf-8" />
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<script src="Scripts/jquery-3.1.1.js" type="text/javascript"></script>
<script src="launcher.js" type="text/javascript"></script>
</head>
<body>
<h3>Please select the sprint you wish to launch:</h3>
<select id="selectSprintList">
</select>
<h3>Please select the configuration:</h3>
<select id="selectConfigList">
</select>
<button id="launchSprintButton">Submit</button>
</body>
</html>
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
[code lang=”javascript” collapse=”true” title=”click to expand if the github.com embedding below is not visible.”]
/// <reference path="Scripts/jquery-3.1.1.js" />
(function () {
‘use strict’;
Office.initialize = function (reason) {
$(document).ready(function (reason) {
/// Upon load connect to the server and request
/// the sprints so that we can fill the select
/// field on the form.
makeAjaxCall("GetSprints", null, function (response) {
/** @type {String} */
var data = response.Message.toString();
/** @type {String[]} */
var results = data.split(",");
$.each(results, function (index, value) {
$("#selectSprintList").append("
<option>" + value + "</option>");
})
}, function (error) {
$(document).append("
" + error + "
");
});
makeAjaxCall("GetConfigs", null, function (response) {
/** @type {String} */
var data = response.Message.toString();
/** @type {String[]} */
var results = data.split(",");
$.each(results, function (index, value) {
$("#selectConfigList").append("
<option>" + value + "</option>");
})
});
$("#launchSprintButton").click(function (event) {
/// The user clicked the submit button. Build the URL
/// from the selection in the select control and
/// change the location
var sprint = $("#selectSprintList").val();
var config = $("#selectConfigList").val();
var url = "";
if (sprint != null && sprint != "" &&
config != null && sprint != "") {
// replace the launcher page with the proper sprint folder location,
// but be sure to keep all the query string values – to pass on
// to our OfficeJS page. However, add our one config setting to the end
url = window.location.href.replace("launcher.html", sprint + "/ComposeMessage.html");
url += "&Config=" + config;
// replace the url
location.replace(url);
}
});
});
}
})();
// Helper function to call the web service controller
makeAjaxCall = function (command, params, callback, error) {
var dataToPassToService = {
Command: command,
Params: params
};
$.ajax({
url: ‘api/Default’,
type: ‘POST’,
data: JSON.stringify(dataToPassToService),
contentType: ‘application/json;charset=utf-8’,
headers: { ‘Access-Control-Allow-Origin’: ‘*’ },
crossDomain: true
}).done(function (data) {
callback(data);
}).fail(function (status) {
error(status.statusText);
})
};
[/code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
And here is the code for the controller, now, I cheated and just returned a string value for each call, but this at least help you get the idea of how to build your own list for sprints and configs:
[code lang=”javascript” collapse=”true” title=”click to expand if the github.com embedding below is not visible.”]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace OutlookLauncherDemoWeb.Controllers
{
public class DefaultController : ApiController
{
///
<summary>
/// Service Request – incoming
/// </summary>
public class WebServiceRequest
{
public string Command { get; set; }
public string[] Params { get; set; }
}
///
<summary>
/// Service Response – outgoing
/// </summary>
public class WebServiceResponse
{
public string Status { get; set; }
public string Message { get; set; }
}
[HttpPost()]
public WebServiceResponse Values(WebServiceRequest request)
{
WebServiceResponse response = null;
switch (request.Command)
{
case "GetSprints":
return getSprints();
case "GetConfigs":
return getConfigs();
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
The idea here is that this add-in is a baseline for yours. You can build these components into a debug build of your add-in and it will call into each separate folder/sprint where you have posted previous versions.
As I start to delve deeper into the world of Office.JS and JavaScript, I am having some fits with how things are done and how things are implemented. Besides getting used to a whole new load of terms, finding out that there are more frameworks than there are developers (ok, maybe that is a small exaggeration), or that JavaScript is not “really” object-oriented (and that’s “ok”) is just nearly too much to handle.
Then there is one of my biggest complains with JavaScript is it use of types. You can get in a lot of trouble here and you can even break JavaScript from its root if you really prototype something wrong (see monkey patching). Additionally, scope can be an issue. And then there is Visual Studio 2015 doing it’s best to try to help you with types, but then it fails you. For example, you type “something(dot)” and you expect IntelliSense to come to the rescue – and low and behold, you get what I like to call: “The yellow bangs of hell.“(tm)
I have been coding with this problem thinking I am all alone in this world screaming every time an object, string, or number fails at IntelliSense. Then on an internal Microsoft email thread where the virtues of TypeScript and JavaScript were being contemplated, a whole world was opened before my eyes. Enter JSDoc. Turns out there is an entire specification around this. And even better – Visual Studio 2015 support it. And it is pretty easy to use. Plus type definition is just the tip of the iceberg, there is a LOT more for me to lesrn. But for now, here is an example of how to define a few common types I frequently use:
[code language=”javascript”]
/** @type {XMLDocument} */
var xmlDoc = $.parseXML(ewsResult.value);
/** @type {string} */
var theString;
/** @type {number} */
var myNumber;
[/code]
I have started to use this very recently in my proof of concepts I work on with my customers and I think I just increased my productivity by 50% (or more). Holy cow.
As you might know (or I hope you do by now), Office Web Add-ins are the new thing. And if you have recently started a project in this new paradigm, you know that it is quite different from Office COM/VSTO/VBA based coding. There is a little bit of a learning curve and some of the new code patterns (namely async) make doing things “the old way” not quite possible. As such this can make getting your head around this programming model a tad tough. And then there is JavaScript too.
One of my colleagues, Michael Zlatkovsky (a member of the Office.JS development team at Microsoft) is writing a book: Building Office Add-ins using Office.JS.
I have had an opportunity to read this book and believe it to be a great resource to understanding the new model. Some of the best parts of the book are where he covers the concepts behind the model, its use of async (and why), how to get around some of the challenges of async with Promises (great chapter), the way to use the Office context objects with some great code patterns, and much, much more.
A customer I was working with wanted help developing a fully on-premises Outlook web add-in. By this, they wanted no part of it to reach out to the Internet (Azure or Office 365). They wanted:
To connect to their internal Exchange server
An internal IIS website
And no references to the Internet (including the Office.js).
This is the topology we are trying to achieve:
If you have developed an Office Web Add-in lately, you find it is inherently biased to the Internet. Even the samples and solutions provided assume Office 365/Exchange and Azure websites. In a default, new Visual Studio solution, the links to the Office.js libraries and stylesheets are all pointing to the web. And, so as you might expect, there are some challenges to getting it to work on-premises only.
This posting covers what you must do to get such a solution to work, including getting past some pitfalls.
First, you have to download the Office.js files locally. And especially for Outlook because the Office.js files that are provided by default in your solution folder (“for offline debugging” as part of VS2015U3 or earlier) are missing some features to work with specific builds of Outlook 2013 and Outlook 2016. You will run into some strange “type” missing and “Office not defined” errors if you forget this step.
Once you have downloaded the Office.JS files, you will delete all the files under the Scripts\Office\1 folder and copy in the contents you downloaded in step 1.
Comment out that line and add the following two lines:
[code language=”html”]
<!– <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript></script> –>
<script src="../../Scripts/Office/MicrosoftAjax.js" type="text/javascript"></script>;
<script src="../../Scripts/Office/1.1/office.js" type="text/javascript"></script>;
[/code]
Once you have developed your solution, you must setup your IIS server. In general here is what you must do:
The IIS Server must have ASP.NET installed, it must have .NET4 installed and you must have the Web Application role enabled.
Open IIS Manager
Create a site, and figure the folder path
Convert the site to an Application
Apply an SSL certificate that is already trusted on all your client computers or that has a root certificate authority that is trusted on all your client computers. If you browse to your site using HTTPS and you get a RED warning about an untrusted site, then the certificate is not trusted or properly setup.
Next, and this was a major issue to troubleshoot, your Exchange Web Services certificate cannot be expired. If it is, any EWS call you make will return “succeeded” but will be blank – missing data. Digging into the logs you might find an error: “ErrorInvalidClientAccessTokenRequest” The Microsoft Exchange Server Auth Certificate that is used for OAuth needs to be updated. To do this you have to be logged into the Exchange server as administrator:
Run this cmdlet to identify the thumbprint of the certificate being used for OAUTH:Get-AuthConfig | FT currentcertificate*
Run this cmdlet to identify the thumbprint of certificates used for other Exchange servers (IIS, SMTP, etc.):Get-ExchangeCertificate | Fl *thumb*
Run these cmdlets to configure Exchange to use the valid certificate (copy/paste the thumbprint from the Get-ExchangeCertificate output):$today = Get-Date
Set-AuthConfig -NewCertificateThumbprint newthumbprint -NewCertificateEffectiveDate $today -Force
Run this cmdlet to make sure the changes are published to the environmentSet-AuthConfig -PublishCertificate
Run this cmdlet to verify the certificate thumbprintGet-AuthConfig | FT currentcertificate*,previouscert*
Now you can deploy your solution to IIS, update your manifest to point to the IIS server (do not forget the HTTPS) and then install it on the Exchange Server Control Panel (ECP) under the Organization / Add-ins option as a mandatory add-in.
Finally, and this last bit is important: IF USING OUTLOOK 2013 or OUTLOOK 2016, YOU MUST BE LOGGED INTO WINDOWS ON THE SAME DOMAIN AS YOUR EMAIL. I know, I know… for some folks this sucks. I have reported this to our product team and they are looking into it. If you are not logged into the same domain controller as your email address is registered, you will not see the advertised add-in. It will load in Outlook Web Access (OWA), but will not appear in Outlook 2013/2016. The exact cause of this problem is unknown, but hopefully it will be addressed in a future version of the product (Exchange or Outlook or both).
Setting up for 100% on-premises is difficult, but it CAN be done. There are a lot of steps, but if you follow the above prescription, you should get it to work. In time, I hope to see this process get easier. But in an online world where Microsoft Office 365 and Azure are main focus, “old fashioned” on-premises solutions are going to require a little more elbow grease.
NOTE: This entry was contributed to by Arthel Bibbens (MSFT) / Exchange PFE. You can follow his posts on this topic here:
I recently worked with a developer to deploy an Office add-in within an Exchange 2013 on-premises environment. This project highlighted a capability of Exchange and Outlook that is a huge shift in the way mail add-ins are developed, deployed, and maintained. Let’s take a look at the key components of Exchange 2013 that support this…