The Most Anticipated OfficeJS Feature is Here

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:

Next, you need to then use PowerShell to set/configure the policy and then apply it to an account.

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:

Here is what you manifest will need to look like:

[code lang=”xml” collapse=”true” title=”click to expand if the embedding below is not visible.”]
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides&quot; 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&quot; 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]

<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>
view raw OnSend.xml hosted with ❤ by GitHub

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.

Here is how you define it:

[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
var sendEvent;
function onSendEvent(event) {
///
<summary>
/// OUTLOOK SEND EVENT
/// Entry point for Message onSend event
/// Per example: https://github.com/OfficeDev/Outlook-Add-in-On-Send/tree/master/outlook-add-in-on-send
/// Setup mailbox policy first:
/// 1) Get sessions and commands: https://technet.microsoft.com/en-us/library/jj984289(v=exchg.160).aspx
/// 2) Set policy and more info: https://dev.office.com/docs/add-ins/outlook/outlook-on-send-addins
/// </summary>

/// <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]

var sendEvent;
function onSendEvent(event) {
/// <summary>
/// OUTLOOK SEND EVENT
/// Entry point for Message onSend event
/// Per example: https://github.com/OfficeDev/Outlook-Add-in-On-Send/tree/master/outlook-add-in-on-send
/// Setup mailbox policy first:
/// 1) Get sessions and commands: https://technet.microsoft.com/en-us/library/jj984289(v=exchg.160).aspx
/// 2) Set policy and more info: https://dev.office.com/docs/add-ins/outlook/outlook-on-send-addins
/// </summary>
/// <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);
}
view raw OnSend.js hosted with ❤ by GitHub

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]

function asyncCompelte() {
// procress the result
if(isOkToSend() == true) {
sendEvent.completed({ allowEvent: true }); // send it
} else {
sendEvent.completed({ allowEvent: false }); // block it
}
}

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.

UPDATE: Here it my post with the demo:
Outlook OnSend and Dialog Sample

But I wanted to get this information out here for now. In the meantime, you can use this demo as an example of how to implement this end-to-end:

Docs.com is Retiring

I went all out using Docs.com for my code starting last year. But, I just got notified that it is being retired this December. wlEmoticon-cryingface.png

However, effectively immediately, all the code pages I was using now display an error like this all over my site:

docs.PNG

This is VERY frustrating I know. wlEmoticon-sadsmile.png

I have decided I will be converting all my code blocks over to Gist. Unless anyone has a better idea for a WordPress site. P.S. I really do NOT want to use add-on’s either. Anyway, this may take me a while as I have a LOT of code content out there on Docs.com. So, please bear with me.

Also, please note, I (kind of) anticipated this. In all my code pages there is a link above every Docs.com embedding you can click to expand and to see the full code on my site:

link.PNG

Office.JS Q&A on stackoverflow

Recently I have become aware that many of our Office.JS folks at Microsoft spend time answering questions on stackoverflow. I want to call out that there is a specific office.js category where you can get your questions answered.

I hope to spend a little of my time there each week answering some questions as well. I have already asked on and got a swift response.

JSDoc – Wow, I just learned something cool

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) wlEmoticon-hotsmile.png

yellow-bangs-of-hell.png

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. wlEmoticon-winkingsmile.png

Downloading latest Office.js for Local (updated)

I have a customer that does not approve of build add-ins that point to the Internet. They want to develop a JavaScript Add-in for Office, but they cannot have it point to and download files from the Content Delivery Network (CDN). So we are forced to just point to the offline copies of the Office.js libraries. However, the version that ships with the default Visual Studio 2015 Update 3 install is version 1.1.0.9. And because we cannot use the NuGet packager in Visual Studio, from their network (because it was blocked), it forced me to get the files a different way. So, I got to a machine where I could use the command line NuGet tool. I placed this in a folder called C:\apps and opened a command prompt (cmd.exe). From there I typed in the commands:

[code language=”powershell”]

cd c:\apps
nuget.exe install Microsoft.Office.js

[/code]

As of this writing (updated on May 8th, 2017) this created a folder in the C:\Apps folder called Microsoft.Office.js.1.1.0.12. That was it. Simple. I then pulled over those files as a zip to the customer project a installed them.

NOTE: The NuGet version of the Office.js libraries are not necessarily the newest. The latest are always available on the CDN, however there is not a way to pull those copies down for a local version. The NuGet version is updated with each point release, but it will lag at some interval behind what is available on CDN.

 

Debugging an Office Web Add-in in Production

It is sometimes tough to determine what is happening in a production environment and you need to get logging information from the add-in to see what is happening. How can you do that?

One way is to build a console.log() option into your add-in that looks for a Debug flag in the manifest. So, you will create two manifest, one that enabled Debugging and another than disables it. More on that in a bit. To start, here is the basic class I created in order to handle this:

[code lang=”javascript” collapse=”true” title=”click to expand if the github.com embedding below is not visible.”]
/*!
* logger JavaScript Library v1.0.1
* http://davecra.com
*
* Copyright David E. Craig and other contributors
* Released under the MIT license
* https://tldrlegal.com/license/mit-license
*
* Date: 2016-08-09T12:00EST
*/
var console = (function () {
"use strict";

var console = {};

console.initialize = function () {
///
<summary>
/// Add a textarea/console to the bottom of the page and then setup the logger
/// </summary>

/// <param name="DebugMode" type="Boolean">If debug mode enabled – we show the console for logging</param>
var debugMode = getParameterByName("Debug") == "true";
if (debugMode) {
// add the console to the screen
$("body").append("<textarea id=’log’ style=’width:100%’ cols=’2000′ rows=’7′ wrap=’off’></textarea>");
$("body").append("<button id=’saveLog’>Copy Log to Clipboard</button>");
$("#saveLog").click(function () {
var field = $("#log");
field.select();
document.execCommand("copy");
});
}

console.log = function (msg) {
///
<summary>
/// GLOBAL: Logs to the textarea on the page
/// </summary>

/// <param name="msg" type="string">The message to log</param>
if (debugMode) {
var d = new Date(Date.now());
var current = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
var data = $("#log").val();
$("#log").val(current + " – " + msg + "\r\n" + data);
}
};
}

function getParameterByName(name) {
///
<summary>
/// Get a parameter form the URL
/// </summary>

/// <param name="name" type="String">Name of the parameter to get from the query string</param>
/// <returns type="String">Value of the paramater</returns>
var url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return ”;
return decodeURIComponent(results[2].replace(/\+/g, " "));
}

return console;
})();
[/code]


/*!
* logger JavaScript Library v1.0.1
* http://davecra.com
*
* Copyright David E. Craig and other contributors
* Released under the MIT license
* https://tldrlegal.com/license/mit-license
*
* Date: 2016-08-09T12:00EST
*/
var console = (function () {
"use strict";
var console = {};
console.initialize = function () {
/// <summary>
/// Add a textarea/console to the bottom of the page and then setup the logger
/// </summary>
/// <param name="DebugMode" type="Boolean">If debug mode enabled – we show the console for logging</param>
var debugMode = getParameterByName("Debug") == "true";
if (debugMode) {
// add the console to the screen
$("body").append("<textarea id='log' style='width:100%' cols='2000' rows='7' wrap='off'></textarea>");
$("body").append("<button id='saveLog'>Copy Log to Clipboard</button>");
$("#saveLog").click(function () {
var field = $("#log");
field.select();
document.execCommand("copy");
});
}
console.log = function (msg) {
/// <summary>
/// GLOBAL: Logs to the textarea on the page
/// </summary>
/// <param name="msg" type="string">The message to log</param>
if (debugMode) {
var d = new Date(Date.now());
var current = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
var data = $("#log").val();
$("#log").val(current + " – " + msg + "\r\n" + data);
}
};
}
function getParameterByName(name) {
/// <summary>
/// Get a parameter form the URL
/// </summary>
/// <param name="name" type="String">Name of the parameter to get from the query string</param>
/// <returns type="String">Value of the paramater</returns>
var url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
return console;
})();

view raw

console.js

hosted with ❤ by GitHub

To enable logging you will add the following code to your initialize:

[code language=”javascript”]

console.initialize();
console.log("Started…");

[/code]

Once initialized this will add a TEXTAREA to the bottom on the page where the log entries will be loaded. Additionally, it will place a “Copy To Clipboard” button at the bottom that when clicked will copy the contents of the TEXTAREA to the clipboard so that they can be forwarded to you as needed.

Once implemented and initialized, you can add a console.log() anywhere you want in your code to add an entry to the log. Now, how do you turn this on. What this is doing in initialize is to see if the debug flag is set in the Query String of the SourceLocation setting in the Manifest. To turn on debugging, you change the following line as such:

[code language=”xml”]

<DesktopSettings>
<SourceLocation DefaultValue="~remoteAppUrl/MessageRead.html?Debug=true"/>
<RequestedHeight>250</RequestedHeight>
</DesktopSettings>

[/code]

That is it. From this you will be able to share two manifests with your users/administrators. The first one will be your default production manifest and the second one can be loaded if you need debugging information from the add-in.

Office JavaScript API Code Explorers

Recently while preparing an internal Chalk Talk on Office Web Add-in Development, a co-worker presented me with two links I had not seen before and I wanted to share them with everyone:

These Code Explorers are pretty cool in that they contain some common use code patterns that you might find useful in your projects.

excelCodeExplorerCapture

Unfortunately, there does not appear to be one for PowerPoint and or Outlook yet. But the fact they are there for Excel and Word is pretty cool.

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:

[code language=”html”]
<id="refresh-button">Refresh</button>
[/code]

Then you wire it up like this:

[code language=”javascript”]
$(‘#refresh-button’).click(function () {
location.reload();
});
[/code]

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:

[code lang=”javascript” collapse=”true” title=”click to expand if the docs.com embedding below is not visible.”]
// 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);
})
};
[/code]


// 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);
})
};

view raw

makeAjaxCall.js

hosted with ❤ by GitHub

Here is a sample of my web service controller:

[code lang=”javascript” collapse=”true” title=”click to expand if the docs.com embedding below is not visible.”]
/// <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;
}
[/code]


/// <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:

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

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

easyEWS.js for Outlook Add-ins

If you have done any work with Outlook Add-ins using the Office JavaScript API’s, you might have found a nifty function that allows you to poll the Exchange Server using EWS calls. The function: makeEwsRequestAsync(). However, this function is not the easiest thing to use. You have to formulate an EWS SOAP message that you send to the service. Getting those correct, writing the code for them, and processing the results are a real beast. But, even worse is finding exactly how to formulate the SOAP message from the existing documentation. It is something I personally did NOT look forward to as I was working on my customers solutions.

My frustration is your benefit (I hope). I created a JavaScript class called easyEws, that makes certain calls very easy. I posted the project on GitHub (previously, I had posted this on CodePlex):

https://github.com/davecra/easyEWS

I have attempted to make the functions a lot easier to use. Here are a few examples:

This code will get you the folder ID for the Drafts folder:

[code language=”javascript”]
easyEws.getFolderId(&quot;drafts&quot;, function (value) {
app.showNotification(&quot;Drafts folder ID: &quot; + value);
});

[/code]

Or, this example which will connect to the Inbox and tell you how many items are there:

[code language=”javascript”]
easyEws.getFolderId(&quot;inbox&quot;, function (folderId) {
easyEws.getFolderItemIds(folderId, function (arrayOfIDs) {
app.showNotification(&quot;There are &quot; + arrayOfIDs.length + &quot; items.&quot;);
});
});
[/code]

easyEWS has the following commands that encapsulates the makeEwsRequestAsync() calls and the SOAP messages:

  • expandGroup: one dimensional expansion of a group (does not do groups within group expansions).
  • findConversationItems: returns a list of mail items that all share the same conversationId.
  • getEwsHeaders: gets a list of X-Headers in the mail message.
  • getFolderId: returns the folder ID for a named folder, like “Drafts”, “Inbox”, etc.
  • getFolderItemIds: returns a list of mail item IDs in a given folder.
  • getFolderProperty: gets a named property from a folder.
  • getMailItem: returns a mail item from the given Id.
  • updateEwsHeader: Updated the named x-header in the message.
  • updateFolderProperty: Updates the property of a folder by the given ID.