Office.js: A Face palm moment with Outlook Client in Compose Mode

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.

EWSEditor

The EWSEditor tool has been around a while and is managed/developed by some of my co-workers that sit on the Outlook Developer Support Team. As I have been developing more and more Office Web Add-ins for Outlook, I have found knowing and using EWS to be a very important skill.

EWSEditor helps in this regard. It is a very powerful, full featured EWS test bed. To get started you download this and extract the contents of the ZIP to a folder. From there you launch it and from the File menu, click Next Exchange Service enter in your email address and then select password and click Ok. Then viola, you are connected:

ewsCapture1.PNG

Once connected, you can start browsing your mailbox using EWS. To get to your Inbox, for example, you select TopOfInformationStore and then select Inbox:

ewsCapture2

From there you can go to different folders and look at the items, properties, and values stored in your mailbox. It is quite handy to understand how these things are structured and stored.

Next, you can click  Tools, EWS Post and test your EWS skills. What I did was entered in my information to connect to my server and filled it in as such:

ewsCapture5

I then entered the following XML:


<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:GetFolder>
<m:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id='inbox'/>
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>

And when I hit run, I got a response you see above. It is that simple. And there are also lots of examples as well. If you click Load Example you will see a lot of XML SOAP requests you can test with:

ewsCapture4.PNG

Download it and give it a try.

 

 

EWS: Sending Email with Attachment

A requirement in an Outlook Web Add-in I am working on required the ability to send an email to an alias with another email as an attachment. I found this a bit challenging as the makeEwsRequestAsync() function limits us to only a handful of EWS functions (EWS operations that add-ins support). This means I was unable to use a lot of the samples I found on the web. However, with the UpdateItem method, I found a way. wlEmoticon-hotsmile.png

First, let me say, I have been asked – why is easyEWS.js not on GitHub. Well, now it is:

https://github.com/davecra/easyEWS

Additionally, you can now reference the latest and greatest in your project at https://raw.githubusercontent.com/davecra/easyEWS/master/easyEws.js.

Next, so I updated the functionality in easeEws via two function:

  • easyEws.getMailItemMimeContent – we use this function to get the MIME content of a specified mail item by the ID.
  • easyEws.sendPlainTextEmailWithAttachment – this function, although a tad more complicated, will create a very simple plain text email and add an attachment send it and save it to the drafts folder.

Here are the two new updates:

    // PUBLIC: creates a new emails message with a single attachment and sends it
    // RETURNS: 'success' is compelted successfully 
    easyEws.sendPlainTextEmailWithAttachment = function (subject, body, to, attachmentName, attachmentMime, successCallback, errorCallback) {
        var soap = '<m:CreateItem MessageDisposition="SendAndSaveCopy">' +
                   '    <m:Items>' +
                   '        <t:Message>' +
                   '            <t:Subject>' + subject + '</t:Subject>' +
                   '            <t:Body BodyType="Text">' + body + '</t:Body>' +
                   '            <t:Attachments>' +
                   '                <t:ItemAttachment>' +
                   '                    <t:Name>' + attachmentName + '</t:Name>' +
                   '                    <t:IsInline>false</t:IsInline>' +
                   '                    <t:Message>' +
                   '                        <t:MimeContent CharacterSet="UTF-8">' + attachmentMime + '</t:MimeContent>' +
                   '                    </t:Message>' +
                   '                </t:ItemAttachment>' +
                   '            </t:Attachments>' +
                   '            <t:ToRecipients><t:Mailbox><t:EmailAddress>' + to + '</t:EmailAddress></t:Mailbox></t:ToRecipients>' +
                   '        </t:Message>' +
                   '    </m:Items>' +
                   '</m:CreateItem>';

        soap = getSoapHeader(soap);

        // make the EWS call 
        asyncEws(soap, function (xmlDoc) {
            // Get the required response, and if it's NoError then all has succeeded, so tell the user.
            // Otherwise, tell them what the problem was. (E.G. Recipient email addresses might have been
            // entered incorrectly --- try it and see for yourself what happens!!)
            var result = xmlDoc.getElementsByTagName("ResponseCode")[0].textContent;
            if (result == "NoError") {
                successCallback(result);
            }
            else {
                successCallback("The following error code was recieved: " + result);
            }
        }, function (errorDetails) {
            if (errorCallback != null)
                errorCallback(errorDetails);
        });
    };

    // PUBLIC: gets the mail item as raw MIME data
    // RETURNS: the entire email message as a MIME Base64 string 
    easyEws.getMailItemMimeContent = function (mailItemId, successCallback, errorCallback) {
        var soap =
            '<m:GetItem>' +
            '    <m:ItemShape>' +
            '        <t:BaseShape>IdOnly</t:BaseShape>' +
            '        <t:IncludeMimeContent>true</t:IncludeMimeContent>' +
            '    </m:ItemShape>' +
            '    <m:ItemIds>' +
            '        <t:ItemId Id="' + mailItemId + '"/>' +
            '    </m:ItemIds>' +
            '</m:GetItem>';
        soap = getSoapHeader(soap);
        // make the EWS call 
        asyncEws(soap, function (xmlDoc) {
            //var content = xmlDoc.getElementsByTagName("MimeContent")[0].textContent;
            successCallback(xmlDoc);
        }, function (errorDetails) {
            if (errorCallback != null)
                errorCallback(errorDetails);
        });
    };

Additionally, you no longer need to call initialize on easyEws. And you can simply add a reference to it in your HTML to always get the latest:

<script src=”https://raw.githubusercontent.com/davecra/easyEWS/master/easyEws.js type=”text/javascript”>script>

Once added, you can use EasyEWS.js on your projects. In my particular project, I used the following code, which does the following:

  • Gets the current mail item (entirely) as a base64 encoded string. This is the full MIME representation of the message.
  • It then creates a new mail item and sends it with a comment from the user to the email address specified with an attachment (which is the base64 string we got in the first step).

This is all done with EWS and because of easyEWS.js, it is done in very few lines of code:

    // Loads the form items to attach to events
    function loadForm() {
        $("#forward-button").click(function () {
            getCurrentMessage();
        });
    }

    // This function handles the click event of the sendNow button.
    // It retrieves the current mail item, so that we can get its itemId property
    // ans also get the MIME content
    // It also retrieves the mailbox, so that we can make an EWS request
    // to get more properties of the item. 
    function getCurrentMessage() {
        var item = Office.context.mailbox.item;
        itemId = item.itemId;
        mailbox = Office.context.mailbox;
        try{
            easyEws.getMailItemMimeContent(itemId, sendMessageCallback, showErrorCallback);
        } catch (error) {
            showNotification("Unspecified error.", err.Message);
        }
    }

    // This function is the callback for the getMailItemMimeContent method
    // in the getCurrentMessage function.
    // In brief, it first checks for an error repsonse, but if all is OK
    // t:ItemId element.
    // Recieves: mail message content as a Base64 MIME string
    function sendMessageCallback(content) {
        var toAddress = "bob@contoso.com";
        var comment = $("#forward-comment").val();
        if (comment == null || comment == '') {
            comment = "[user provided no comment]";
        }
        try{
            easyEws.sendPlainTextEmailWithAttachment("Message with Item Attachment",
                                                     comment,
                                                     toAddress,
                                                     "Email Attachment",
                                                     content,
                                                     successCallback,
                                                     showErrorCallback);
        }
        catch (error) {
            showNotification("Unspecified error.", err.Message);
        }
    }

    // This function is the callback for the easyEws sendPlainTextEmailWithAttachment
	// Recieves: a message that the result was successful.
    function successCallback(result) {
        showNotification("Success", result);    
    }

    // This function will display errors that occur 
    // we use this as a callback for errors in easyEws
    function showErrorCallback(error) {
        showNotification("Error", error);// .error.message);
    }

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:

easyEws.getFolderId(&quot;drafts&quot;, function (value) {
	app.showNotification(&quot;Drafts folder ID: &quot; + value);
});

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

easyEws.getFolderId(&quot;inbox&quot;, function (folderId) {
	easyEws.getFolderItemIds(folderId, function (arrayOfIDs) {
		app.showNotification(&quot;There are &quot; + arrayOfIDs.length + &quot; items.&quot;);
	});
});

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.