This blog post is more for me than you. I come from an era when command line was the only way to get things done. The folks that were able to do that in the 1980’s and early 1990’s, were called…
Well, for some reason, after advancements in GUI (Graphical User Interfaces) we are back to command-line interfaces. I have heard an argument that this makes it easier to build in voice command interfaces, but I do not see myself saying: “npm install”. I believe it came about because it is easier to create a command line interface quickly for free than it is to create a graphical one. Just sayin’.
With that said, lets get down the business. I published OfficeJs.dialogs to NPM and found a few last minutes bugs and needed to update it. Well, this seemed easy enough, but I was getting some error about my git working directory:
git working directory dialogs not clean
This turns out to happen because I needed to do both a push and a pull. Not sure why, but I did a push and sync and that did not resolve the issue. I had to do a pull from git too:
Next, I had to then do a version update. To do this you type this in the Terminal window in VS Code once it is pointed to the repository folder:
npm version patch
This will add a single point to the version, so for me that was from 1.0.1 to 1.0.2. Next, you issue a single command:
npm publish
This will publish version 1.0.2 (in my case) to the node module in the sky (npmjs.com). Next, I then went to my project where I am using it and then issue this command:
npm update officejs.dialogs
And just like that, I had the latest version. Seems simple enough, but it took a while for me to research this and get it working. And since these are all command lines without a graphical ability to “hunt and peck” for the right menu item, I am writing it down for posterity. I will likely be referring to this post for myself again in the future.
Several customers have asked me if OfficeJS has something similar to a Visual Basic or C# MessageBox.Show() function. The answer is no. And for a long time there was not even a dialog option. With the latest releases of the OfficeJS libraries comes a new dialog feature. Yet to get a standard MessageBox, you will still need to create it from scratch. Or, at least until this blog post you did. I have created a helper library the consists of two files:
dialogs.js
dialogs.html
To reference this library you can do any of the following:
NOTE: This assumes your page is in the root of your project. The key point is that it is added to your node_modules when you use NPM and this is how you will reference it.
Once referenced you can then call a MessageBox like this:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
MessageBox.Reset();
MessageBox.Show("This is a test with a lot of text that simply has not point but to show you what " +
"happens when you place a lot of text in a MessageBox for any point other than to " +
"place a whole lot of text in the MessageBox.",
"This is the Caption",
MessageBoxButtons.AbortRetryCancel,
MessageBoxIcons.Stop,
true, "Check this if you want me to stop nagging you.",
function(buttonPressed, checked) {
console.log("The button pressed was: " + buttonPressed);
console.log("The checkbox was checked? " + checked);
});
[/code]
This file contains 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 embedding below is not visible.”]
InputBox.Reset();
InputBox.Show("What value do you want to enter?",
"InputBox caption",
"Default value", function(result) {
var msg = "";
if(result.length == 0) {
msg = "The user pressed cancel.";
} else {
msg = "The user entered: " + result;
}
console.log(msg);
});
[/code]
This file contains 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
You can show a custom form of your own design like this:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
Form.Reset();
Form.Show("/test.html",10,20,false,function(result){
var yourJSON = JSON.parse(result).Result;
// if you placed false in the 4th param you must
// handle the close of the form
Form.DialogClose();
});
[/code]
This file contains 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 an example of what the above code looks like:
It is important to note that like everything else in the OfficeJS world this is an async dialog. This also means it is non-blocking. This means any code you do not have in your callback method will continue to run. And if you are wanting to display multiple message boxes at the same time – you cannot. The last one you try to display wins, the others will be gone. Most everything in this dialog is just like you will remember from the good ol’ Visual Basic/C# MessageBox and InputBox. Even the constants for MessageBoxButtons and MessageBoxIcons are the same. But, I added a little flare and it probably helps with the best practices in OfficeJS to not nag the user with dialogs, and that is the ability to add a check box to the form so you can ask the user if they do not want to be bothered by your code anymore.
For the MessageBox, the withcheckbox and checkboxtext are there to give you that ability. Additionally, you see the callback method (asyncResult) that will return once the use dismissed the dialog. It will return with two pieces of information:
The button the user clicked in string format. So “Yes” or “Cancel” will be what you see here.
A Boolean representing whether the check box was checked or not.
For the InputBox, the callback method (asyncResult), will return one piece of information. If will return the text the user entered, or it will return nothing (an empty string), if the user pressed cancel.
The Form method will return a JSON object:
Error: { }, // Error object
Result: { }, // JSON from form
Cancelled: false // boolean if formed cancelled with X
The Result object will be the JSON from your own form. In your code you will need to call back to the parent like this:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
Office.initialize = function(reason) {
$(document).ready(function () {
$("#okButton").click(function() {
// notify the parent
Office.context.ui.messageParent("My custom message.");
});
});
};
[/code]
This file contains 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
You will also see in the examples above, I call .Reset() before I issue a new dialog request. This is because the objects are global and this is a way to be certain to clean up anything in memory that might be associated with a previous dialog. In my testing, I never really had problems like this, but I added it as an extra precaution.
Also, note, I have only tested this in Outlook OWA, I have not had a chance to test it in Excel, Word, PowerPoint or even in the full Outlook client. So, if you encounter issues in those other clients, please let me know.
Finally, I want to call out the OfficeJS Helpers. This library provides a lot of help with authorization dialogs, but also has a simple method for displaying messages using OfficeHelpers.ui.notify(). You can install it into your project using NPM:
As I delve ever deeper into the world of Node, I have found the ability to install packages with NPM quite handy. I do this from VS Code using the Terminal window. I just type:
npm install <packagename>
As I have been developing different packages for my customers, I have found the need to install code that I have been reusing over and over again. Most importantly, easyEws. So, I created a npmjs account (davecra) and I published easyEws. But what is even better and what I was after, is I can now install the latest version of easyEws by going into the Terminal window in VS Code and typing this:
I am “newish” to web development in the modern era. By modern era I mean, that I was writing webpages before “getting on the web” was cool. Before the movie “You Got Mail” came out, when AOL, dialup and Netscape were all still “the thing.” So, I know web development back then, probably up through 2007’ish. And I have done very little serious web development until 2 years ago when I really started delving into OfficeJS. So here are some things I have learned and would like to admit I did not know… I mean, pass along to you:
Node. This heralds back to my previous post on Yo Office. In this post, I call out that NodeJS/BrowserSync is a powerful feature. I can safely say, as I have been working with it more and more, it has changed my life for the better. I have also been using Node and VS Code in an effort to understand this newer way of development. I have had a number of colleagues convince me to keep on keeping on with it – it will grow on me, they swear. Ok… So, what I like is when I hit save in VS Code and go back to the browser and click my add-in button (for a function-file), or open my taskpane again – it is current. I DO NOT have to refresh the browser. Browser-Sync takes care of all this for me.
Chrome. If you are debugging outside of Visual Studio, I have found Chrome to be the best browser with which to debug. Here is why:
It’s Incognito mode is the best option for making sure you always have the most recent files and things are not cached. Edge and IE have private modes, but there is the next issue…
Edge and IE are tied into Windows and when you try to log into your developer tenant from them, it automatically grabs your credentials from Windows and takes you to your company email. Ugh!
Incognito still lets you (with warnings) load certain extensions and you also get form fill, so it makes getting into your tenant mailbox super quick and easy.
F12, developer tools. Edge and IE also have, but look at Chrome’s: Enough said there.
console.log(). This is a bit of a no brainer, but I have found that sprinkling my code with this helps me get a good idea of my code flow, especially with Async() hell that you can get into with OfficeJS.
debugger; – this has saved my life. I know, I am a “noob,” but I had no idea how to set a breakpoint in my code and get the browser to stop there, right there… no not there, RIGHT THERE!!! There is a window in my office with easy access to the pavement 4 stories below. One day, the day when I found this (after much frustration with my inability to write coherent JavaScript – a common problem I have), I was about to see if I could bounce. So, what you do is place this one word in your code, and if you have the developer tools open… viola! OMG:
debugger;
try/catch – In C#, especially with the debugger attached, I sometimes worried about error handling after the fact. Partially because I was not sure all the exception types I would want to catch or just use a general exception handler. But to say unexpected things happen in JavaScript and OfficeJS all the time is an understatement. Especially if you pretend to be a JavaScript developer like me.
I use the console to always output errors.
And, I know this is coming, TypeScript makes things so much easier in this area. As a matter of fact, I know one colleague that will be telling me “of course you do not bounce,” and my problems would be so much less had I used TypeScript in the first place. For the record, TypeScript makes a horribly disastrous JavaScript developer like me into an only moderately bad one. But, unfortunately, all my clients are writing code in JavaScript, so here I must suffer.
I have been working on a number of projects for my customers and recently, dialogs have taken front and center stage. The Office.context.ui Dialogs are powerful, albeit a tad confusing and the documentation suffers from a few easily missed points. Here is the documentation:
But in this post, I hope to explain everything I have learned. To start off with here is the code to issue the dialog:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
Office.context.ui.displayDialogAsync(‘https://localhost:3000/function-file/dialog.html’,
{ height: 20, width: 30, displayInIframe: true },
function (asyncResult) {
dialog = asyncResult.value;
// callbacks from the parent
dialog.addEventHandler(Office.EventType.DialogEventReceived, processMessage);
dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
});
[/code]
This file contains 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 first one is an event receiver and really this is the event handler for errors, such as being unable to open the dialog or, most importantly, the user closed the dialog by clicking the (X) in the upper right of the dialog. There are a series errors you can catch, but specifically, the dialog cancel is this:
12006
The dialog box was closed, usually because the user chooses the X button.
The second one is a handler for messages coming from the dialog. These messages can be anything, but is usually a string or a JSON string. You can send a message from the dialog like this:
When the dialog issues a message using the code above, the function defined in the event handler is called. For example, if the user clicks an OK button or Submit button, you can pass the stringified values from the form back to the callback function. From there the dialog actually remains open until the caller issues a close, like this:
[code lang=”javascript”]
// close the dialog
dialog.close();
[/code]
In the example above where I make the displayDialogAsync call, you will see I defined the SAME callback function for both dialog events. I did this because the results can be parsed by the same function. Here is what my function look like:
[code lang=”javascript” collapse=”true” title=”click to expand if the embedding below is not visible.”]
function processMessage(arg) {
// close the dialog
dialog.close();
// procress the result
if(arg.error == 12006) {
// user clicked the (X) on the dialog
sendEvent.completed({ allowEvent: false });
} else {
if(arg.message=="Yes") {
// user clicked yes
sendEvent.completed({ allowEvent: true });
} else {
// user clicked no
sendEvent.completed({ allowEvent: false });
}
}
}
[/code]
This file contains 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 sample add-in does is pops up a dialog box anytime you press Send on an email with a question: “Are you sure?” If you click Yes, it sends, if you click “no” is blocks the send. Here is what that dialog looks like:
If you have followed the steps from my Yo Office post, you will be able to open the project in VS Code, and from the terminal, launch it with:
npm start
Next, you will need to follow the steps to enable the OnSend policy in your account and then you will need to install/sideload the manifest. From there you will not see any appearance of the add-in at all, until you press the Send button.
NOTE: At the point I first tested it on June, 13th, 2017, there is a KNOWN ISSUE with the certificates created fro a “yo office” build. If you have not resolved the certificates issue you will be able to start the project in Node.js, but the add-in will fail to run. You need to perform the steps outlined here to correct it:
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 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 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 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.
However, effectively immediately, all the code pages I was using now display an error like this all over my site:
This is VERY frustrating I know.
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: