Autolaunch and Outlook on Windows

I have been updating my 5entences add-in install for use in the AppSource/App Center for Office, plus adding an auto-summarization capability via OpenAI. 😉 COMING SOON!!!!

Originally, this add-in was using the Dialog API inside the “original” OnSend event. This was a bit nifty as it presented the user with a blocking popup that would allow them to manage everything in one place. It told them, they had more than 5 sentences, and would suggest they go back and fix it, or go ahead and send it. But that type of event is NOT supported in the App Store.

Enter Smart Events, and with it specifically the OnMessageSend event. The adventure began…

I have an Office 365 account and installed Office 365 from my portal.office.com page, made sure I was updated, and everything was looking good, except that:

  • When I have my personal Outlook.com email address attached to Outlook, I cannot debug add-ins in Outlook full client on Windows.
  • Even when I did remove my Outlook.com email address, my events were still not firing.
  • Everything worked great in Office on the Web.

So, I proceeded with updating the code using Office on the web to debug, giving up on Outlook full client in the interim. Once I got everything working well in Outlook on the Web, I went back to Outlook on Windows and began to lose my hair.

I quickly discovered that my ES6 code in the command.js, were not working. As you dig into the documentation you find that the WebViewURL and the override for JSRuntime, sharing the same file become an issue. It turns out that even with the WebView2 control installed on my box, and even though I have the latest version of Office 365 full client installed, my JSRuntime code reverts to Trident+ (IE11). I refactored my code to:

  • stop using const, instead var everything.
  • stop using => arrow operators, reverting to full function()
  • stop using async/await, and built pyramids of doom
  • stopped using Promises() even

The reason I reverted, is because I pulled out all transpiling and polyfills because they SLOW down my add-in code, make it too large and it impacts my already overwhelmed server. I also like to remain as pure JavaScript ES6 as I can. I am a bit puritanical, I guess. 😆

But even with all that it did not work. So, line by line I went and found the first problem:

/**
* Ensures the Office.js library is loaded.
*/
Office.onReady((info) => {
/**
* Maps the event handler name specified in the manifest's LaunchEvent element to its JavaScript counterpart.
* This ensures support in Outlook on Windows.
*/
if (Office.context.platform === Office.PlatformType.PC || Office.context.platform == null) {
Office.actions.associate("onMessageSendHandler", onMessageSendHandler);
}
});
view raw onready.js hosted with ❤ by GitHub

That code seemed to work, but the add-in would just hang telling me “…it is taking too long… Try Again.” I resolved it by doing this:

/**
* Checks to see if we are running in Windows Outlook
* @returns {Boolean}
*/
function isPC() {
try {
if (Office.context.platform === Office.PlatformType.PC || Office.context.platform === null) {
return true;
} else {
return false;
}
} catch {
return false;
}
}
if (isPC() === true) {
Office.actions.associate("onMessageSendHandler", onMessageSendHandler);
} else {
Office.onReady(function () {});
// Everything below is for OTHER (non-PC) clients per older constructs. Not certain
// if any of this is needed except for COMMANDS (rather than EVENTS), so eventually
// if commands are added, we have this in place…
var g = getGlobal();
// The add-in command functions need to be available in global scope
g.onMessageSendHandler = onMessageSendHandler; // on send event
}
/**
* OnSend event triggered
* @param {Office.AddinCommands.Event} event
*/
function onMessageSendHandler(event) {
Office.onReady(function () {});
Office.context.mailbox.item.body.getAsync(Office.CoercionType.Text, { asyncContext: event }, function (asyncResult) {
var body = asyncResult.value;
var event = asyncResult.asyncContext;
// … more code here …
});
}
view raw commands.js hosted with ❤ by GitHub

Essentially, I had to put the associate at the root of the file. Without the PC check, this would bomb on Office online and the Mac, so I gated that with the function you see: IsPC(). And Office online needs the Office.onReady() in the root to work effectively, so you see that there. But putting Office.ready() in the root broken Outlook on Windows, but also ignoring it/not using it, gave me the same problem. I discovered that if I put it in the event activation itself, as you see, I was finally able to get into my event handler and execute and Office.context (line of code). However, deeper in my code, it was STILL failing.

Debugging my code further, I had a LOT of Regex to determine what was going on in the body of the email. Introduce Trident+ and IE11 (es5), and you realize that “look heads” and a lot of really cool stuff I have grown accustomed to do not work. So, I struggled and struggled, until I just sent to my friend ChatGPT and asked it to produce me IE11 compatible Regex for each of my Regex statements. And then viola, my add-in was fully functional on Windows and Office online.

I am still a hacker by nature – what works, works. And I have converted from Office Add-ins written in C# with Visual Studio Tools to Office to this OfficeJS paradigm. I spend a lot of time reading the documentation and I SWEAR it tells me that with my version of Office 365 and having the WebView2 control installed, I should not be reverting to IE11 (Trident+) for Smart Events:

Microsoft® Outlook® for Microsoft 365 MSO (Version 2308 Build 16.0.16731.20052) 64-bit

But maybe it is buried in there somewhere that on Windows/PC, it always uses IE11 for Smart Events. Either way, to make sure you support 99% of the market out there, I guess you need to write you code like this or use polyfills and transpile. Bottom line is I got it to work. If anyone else is having similar issues, hopefully this helps. If anyone has an alternative or can point to something I am doing wrong (other than using polyfills and transpile my code), I am open to suggestions.

Excel Send to Trello

Well, that was fast. My latest add-in is published. Excel Send to Trello.

As per my previous post, the thing I found most interesting was how Excel full client seems to fail if you configure your server .htaccess file to prevent caching. Well, I found out that my Outlook Send to Trello add-in actually had the same problem too. The Outlook client just happened to refresh this morning and my icon disappeared there too. In Office online it seems to work, but in the full client you cannot seem to force the client to NOT cache. I see my files all pulled down locally in the Wef folder and my concern is that when I update the add-in it will not go get the latest every time… I have actually seem and beat my head over this problem a few times. But the solution there for anyone working on a BETA site for example making changes and all of a sudden the full-client stops refreshing your updates, this is a good article to keep handy.

But when Wef strikes in production, I have found customers are not so excited to blow away this folder and find all their add-ins, preferences, stored cached credentials and other goodies for each and every add-in are gone. Ergo why I added the no-cahce to the .htacess. Oh well. 🙁

Also, just to share something else as I am delving more and more into publishing add-ins for real. As an Office Developer, in the traditional sense (aka boomer 😛, VBA/VSTO/COM), there are aspects of living in an HTML web world that I still learning (although this is an old one it comes up now and again because I forget something).

You have to worry about various attack vectors and sanitizing HTML strings. There are LOTS of libraries and solutions out there and Mozilla even has documented a possible standard supported in several browsers, but not all. It is a tricky thing because some sanitizers do too much or not enough, and then you also rely on a dependency which has now burned me more often than just owning things that might be a hundred lines of code for my own common library of goodies.

So, I have created my own based on various library implementations, and found the best option is to escape most of the stuff you find “injected” rather than remove it.

/**
* Sanitizes the string for possible malicious values
* @param {String} string
* @returns {String}
*/
static sanitizeString = (string) => {
try {
string = string.replace(/(javascript:|onerror)/gi, "");
string = string.replace(/undefined/gi, "");
string = string.replace(/<script/gi, "&lt;script");
string = string.replace(/<iframe/gi, "&lt;iframe");
string = string.replace(/<object/gi, "&lt;object");
string = string.replace(/<embed/gi, "&lt;embed");
string = string.replace(/<applet/gi, "&lt;applet");
string = string.replace(/<form/gi, "&lt;form");
string = string.replace(/<meta/gi, "&lt;meta");
string = string.replace(/<link/gi, "&lt;link");
string = string.replace(/<a\s/gi, "&lt;a ");
string = string.replace(/<img\s/gi, "&lt;img ");
string = string.replace(/="/gi, "&#x3D;&#39;");
string = string.replace(/='/gi, "&#x3D;&#39;");
string = string.replace(/=`/gi, "&#x3D;&#x60;");
string = string.replace(/\/>/gi, "&#x2F;&gt;");
return string;
} catch (e) {
return `[[SANITIZED STRING MALFORMED: ${e}]]`;
}
};

The important thing is that in the web world, anytime you take data from one service to another, or take input in a field, or grab input from some element on a page and insert it back into another element in your code, there is a hack waiting to happen if you do not sanitize.

New Excel Add-in & Side Loading

I recently published a new Excel Add-in called “Excel Send to Trello.” It is a pretty nifty add-in that will take an Excel sheet full of names, descriptions, and dates and build a bunch of Trello Cards on a specific list in Trello for you. It is just a start (v1.0) and has many features to come based on popularity.

But when I submitted the add-in to AppSource they came back to me with an issue that I was not seeing. Specifically, that the icon on the Ribbon was showing the default add-ins image and not my icon. I did not see this on my “beta” test site that I was using but had sent them a link to my manifest with was right to my production published version. The issue it turns out after a lot of testing was the .htaccess on my production site and the ONLY difference was this line:

<IfModule mod_headers.c>
    Header set Cache-Control "no-store, no-cache, must-revalidate"
</IfModule>

The problem it turns out is that Excel (full client) cannot read your icon without caching it. This works FINE in Excel Online. Oh well. So, I cannot prevent my add-in JS files from being cached which it turns out is a huge problem when I push an update it can take days/weeks for the Office application to expire its own cache. Unless someone know a way around this…

Anway, the way I discovered this was via sideloading the manifest pointing to my production site. And it also turns out I had forgotten how EASY Outlook makes sideloading. In Excel (full client) it is not that easy, because you do not have the option to point to your own manifest, except in Excel Online. Then I recall some work a colleague Marty Andren and I worked on many, many years ago, called the Web Add-in Side loader. And low and behold, it still works like a charm.

I was able to sideload in Excel using the command line tool, then I saw the problem. That is where I began the file-by-file comparison “Beta” to “Production” and found the difference mentioned above.

Anyway, it was an adventure, with a blast from the past! And keep an eye out, my new Excel Add-in will hopefully be published any day now.

Outlook Signature Add-in

I recently got a question about this add-in created as a sample by the Microsoft Office Developer Team. It is provided as a sample and is a challenging bit of code to follow. It all started because I was trying to provide some advice to someone who is new to the OfficeJS world (but not Office development in general). They wanted to build a cross-platform add-in for a cause. After tinkering with it for a few hours, I managed to get it to work. But then I looked it over and it was quite a bit of tinkering on my part.

In most cases I create Add-ins based on React, but because I have done a lot of Trello development of late, I have gotten into more of a pure ES6 vibe. The code provided in the add-in as I stated was hard to follow because I think I am losing my jQuery and ES5 JavaScript skills.

Anyway, as I said, I spent a few hours today and worked on getting it into ES6 format by moving most of the code into classes, removing redundant code, removing redundant HTML pages, consolidating CSS and adding a lot of additional JSDoc comments throughout the code.

Hopefully, this version is a bit easier to follow for those that develop in a more modern ES6 style.

NOTE: This looks and behaves exactly the same, but I have not thoroughly tested it. So, it might be a bit rough here and there. If you encounter issues please let me know. 😁

Determine MIME type from base64

In writing my new Outlook Add-in (Send to Trello), I got stuck on attachments. The first version did not include an attachments option because of two unique problems that compounded each other:

  1. The Office Add-in API no longer provides a Media Type/MIME Type with an attachment request. I am able to get the “blob()” from Office, but other than the file extension there is not a way to determine the type. But sometimes a file does not have an extension, or the extension is wrong, etc.
  2. The Trello API will not let you upload without supplying a MIME type, you cannot just give it a Base64 string as an attachment and let them figure it out.

So, I found out something interesting while researching a workaround. Most every base64 string of a specific file type starts with the same “prolog” of text. Using this, combined with the fallback of the file extensions, I was able to get attachments to work (for the attachment types supported by Trello). So, v1.02 will now include attachments.

Anway, as for the workaround I found, this might be ugly, but wanted to share it anyway:

/**
* Returns the data type based on the base64 string
* @param {String} base64String
* @param {String} fileName
* @returns {String}
*/
detectMimeType(base64String, fileName) {
var ext = fileName.substring(fileName.lastIndexOf(".") + 1);
if (ext === undefined || ext === null || ext === "") ext = "bin";
ext = ext.toLowerCase();
const signatures = {
JVBERi0: "application/pdf",
R0lGODdh: "image/gif",
R0lGODlh: "image/gif",
iVBORw0KGgo: "image/png",
TU0AK: "image/tiff",
"/9j/": "image/jpg",
UEs: "application/vnd.openxmlformats-officedocument.",
PK: "application/zip",
};
for (var s in signatures) {
if (base64String.indexOf(s) === 0) {
var x = signatures[s];
// if an office file format
if (ext.length > 3 && ext.substring(0, 3) === "ppt") {
x += "presentationml.presentation";
} else if (ext.length > 3 && ext.substring(0, 3) === "xls") {
x += "spreadsheetml.sheet";
} else if (ext.length > 3 && ext.substring(0, 3) === "doc") {
x += "wordprocessingml.document";
}
// return
return x;
}
}
// if we are here we can only go off the extensions
const extensions = {
xls: "application/vnd.ms-excel",
ppt: "application/vnd.ms-powerpoint",
doc: "application/msword",
xml: "text/xml",
mpeg: "audio/mpeg",
mpg: "audio/mpeg",
txt: "text/plain",
};
for (var e in extensions) {
if (ext.indexOf(e) === 0) {
var xx = extensions[e];
return xx;
}
}
// if we are here – not sure what type this is
return "unknown";
}

New Outlook Add-in: Send to Trello

I have been using Trello for a while now and one of the features I have found most useful is to take an email I received and turn it into a Kanban item on my backlog to address later. This allows me to archive the email but keeps it on my “Trello radar” as I work at my own pace through my personal backlog.

Recently, Trello removed their add-in from the Microsoft Office store. If you have the add-in installed, you will see this error:

Well, since they say necessity is the mother of all invention and I really had to fill the gap as it is part of my routine, I rolled my own. 🤓 To add a degree of difficulty, I wrote this in VS Code in Linux running in Windows Subsystem for Linux (WSL). See my previous post. It was a fun exercise as I am on vacation and using the time tom learn new things, engage in self-improvement and relax (coding is relaxing to me 🤓🤓🤓). In the end, I learned something and created something for everyone to enjoy.

Say hello to the recently published: Send to Trello Outlook Add-in.

Give it a try and let me know what you think.

Office Web Add-in Debugging in VS Code

Another recent announcement that has me excited is the ability to debug Office Web Add-ins directly from VS Code. Before this recent announcement, it was a hit or miss proposition. There was Visual Studio 2019, that did a pretty good job. But I liked the lightweight simplicity of VS Code. Visual Studio 2019 seemed too too heavy-weight. It’s hard to put my finger on how or why, but I really enjoy VS Code for Web Add-in development so much better. Except for debugging…

So, to debug, I actually did most of my dev/test in the web versions of Office (Excel online, Outlook online, etc.). Then came the Edge Developer Tools Preview which helped debug task pane add-ins in the full clients. But that did not help with things like the On Send event in Outlook or other UI-less functions. So, it was a struggle at times.

Now that has all changed!!!

The process is a tad more complicated than I like, but it does work. Essentially, you need to:

  1. Run VS Code as administrator
  2. Install the extension in VS Code by pressing CTRL + SHIFT + X and searching for the “Microsoft Office Add-in Debugger”
  3. Add the following code to the .vscode\launch.json file to enable Office Debugging in your project. You will need to update line #7, and replace the uppercase HOST text with the host application for your Office add-in.
{ 
   "type": "office-addin", 
   "request": "attach", 
   "name": "Attach to Office Add-ins", 
   "port": 9222, 
   "trace": "verbose", 
   "url": "https://localhost:3000/taskpane.html?_host_Info=HOST$Win32$16.01$en-US$$$$0", "webRoot": "${workspaceFolder}", 
   "timeout": 45000 
}

NOTE: If you create a new Office project with Yeoman, you will not need to add this line, it will be part of the default template going forward.

  1. Press CTRL+SHIFT+D to then open the Debug pane, select Attach to Office Add-in option from the drop down list at the top of the pane, and press F5 to start debugging.
  2. You can now set breakpoints, see variable values, etc.
Attach to Office Add-ins menu option

easyEws v1.0.16 Published

I have made a few minor updates to easyEws. First, was a fix for distribution lists with an “&” in the name. The second is a few minor JSDoc updates for better linting. And finally, I changed a couple forEach loops to traditional for loops for performance reasons.

I have published the update to NPM and to my GitHub.

Please let me know if you have any issues or questions.

OfficeJS.dialogs Updated to v1.0.9

Like yesterday with easyEws, it has been a while since I have touched the OfficeJS.Dialogs library. I updated it yesterday with a quick fix and some content updates. I have had questions about using it from a CDN. The primary issue is that the displayDialogAsync() API does not support CDN. It just displays the HTML as raw text in the dialog. There are some ways I can provide this as a workaround, but it involves me support infrastructure. So, the better thing is to just wait for the API to support CDN’s.

If you have any questions about using this library, please let me know.

How to Fix IntelliSense in VSCode for OfficeJS

I spent the better part of this morning trying to figure out why a new Yeoman generated Outlook add-in was not working with IntelliSense in VSCode. I would type “Office.” and nothing would appear. I would press CTRL+{SPACE} and still nothing.

After a lot of digging and comparing projects that did work to those that did not, I found that the @types were missing. So I then installed @types/office-js:

npm install @types/office-js

Once I did this, viola! IntelliSense started working like a charm. From my experience this has happened to me a few different times, in these given scenarios:

  • Moving an OfficeJS project from VS2017 to VSCode
  • Moving a Script Lab project to VSCode
  • Creating a blank OfficeJS project from scratch with just two files (manifest and webpage).

In my continued research I found that the way VSCode handles types is through NPM and you need to have data type definition files to get it all to work, that is why you need to install both NPM and the OfficeJS @types to get this to work. Per this page:  https://code.visualstudio.com/Docs/languages/javascript

Automatic Type Acquisition (ATA) uses npm, the Node.js package manager, to install and manage Type Declaration (typings) files. To ensure that Automatic Type Acquisition works properly, first ensure that you have npm installed on your machine.

Secondly to that, for OfficeJS development, make sure you have @types/office-js.