Creating Plug-ins for Outlook: JavaScript Plug-in Example

By Dennis Turpitka

A great set of features, even by default, is what made Outlook a popular e-mail client and personal information management system. However, as with any business-focused applications, there is often a need for additional functionality.

Luckily, Outlook allows adding new features by creating your own custom plug-ins. There are two types of plug-ins used, COM and JavaScript, each with its own set of pros and cons.

We will focus on Outlook plug-in development with JavaScript, providing a practical example of how to create a client-side plug-in that checks attachments and adds information about them to the body of the e-mail. As a conclusion, we will compare both types of plug-ins and describe why and where you want to use them.

An example plug-in, described below, is designed to check the following data:

  • Whether any extra addressees been included into a ‘sent to’ field
  • Information on attached files (later it may become possible to check the content of the attachment)
  • Add information to the body of the message

We will demonstrate how to modify the interface by adding an additional button to analyze and send e-mails.

Configuring The Manifest File

Now, let’s try to implement the example described above. The first thing we need to do is to configure a manifest file. The manifest file is used to describe the location and functionality of the additional button that we need to create, as well as necessary permissions. In our case, as soon as the button has been clicked, the code that analyzes the attachment should be called.

There are several things you need to keep in mind:

  • EWS operations (GetItem and SendItem) need ReadWriteMailbox permissions. You can get access to them through the mailbox.makeEWSRequestAsync function.
  • Because the plug-in works only for outgoing messages, the button needs to appear only when you try to create a new e-mail. To do this, we need to set a type for ExtensionPoint in a MessageComposeCommandSurface:
    <ExtensionPoint xsi_type="MessageComposeCommandSurface">
  • To call a specific function when the button has been clicked, we need to set a property:
    <Action xsi_type="ExecuteFunction">
       <FunctionName>sendEmail</FunctionName>
    </Action>
    

By setting this up, the sendEmail function will be called as soon as someone clicks the button.

Additionally, the path to the HTML file needs to be specified. This, in turn, contains the path to the JavaScript file with implemented sendEmail.

<bt:Url id="functionFile" DefaultValue=" YOUR_WEB_SERVER
   /AppCompose/FunctionFile/AttachmentInfo.html"/>

Finally, we need to change all the YOUR_WEB SERVER references in the manifest file to the actual server address with HTTPS protocol.

Adding the Plug-in to Outlook

Once the manifest file has been properly configured, we can use the Web version of Outlook to add the add-in.

To do this, we go into settings (represented by the gear icon in the upper right corner) and click Manage integrations.

Clicking Manage integrations
Figure 1: Clicking Manage integrations

In the next window, we click the ‘Click here to add a custom add-in’ button and then choose our add-in.

Adding a custom add-in
Figure 2: Adding a custom add-in

Once all is set, installation should begin. When it’s successfully completed, the add-in will be located in the My add-ins category.

The "add-ins" installation is complete
Figure 3: The “add-ins” installation is complete

When all is complete, the new button should be visible when you try to create a new e-mail. Once it’s clicked, the attachment will be analyzed and results will be added to the body of the e-mail before it is sent.

The new button is available
Figure 4: The new button is available

Getting Data from the Exchange Server

Now, let’s discuss more thoroughly exactly what happens when the Analyze and Send button is clicked. First of all, the sendEmail function is executed. The path to the file in which this function is declared (Attachementinfor.js) is described in the manifest file via HTML. The manifest file also contains information on what function should be called.

We can act when the sendEmail function receives the event. To gather information about attachments, we need to receive the mail ID, which in turn will allow us to use the EWS request. However, the ID is not assigned to the new e-mail at this point, so we need to save it as a draft. This can be done by calling the saveAsync function:

Office.context.mailbox.item.saveAsync(saveItemCallBack);

The saveItemCallBack function will receive the e-mail ID. Next, we need to create an EWS request to gather information on the attachment:

function getItemDataRequest(itemId) {
   var soapToGetItemData = '<?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_xsd="http://www.w3.org/2001/XMLSchema"' +
         xmlns_soap="http://schemas.xmlsoap.org/soap/envelope/"' +
         xmlns_t="http://schemas.microsoft.com/exchange/services/
            2006/types">' +
      '<soap:Header>' +
         '<RequestServerVersion Version="Exchange2013"
             soap_mustUnderstand="0" />' +
      '</soap:Header>' +
      '<soap:Body>' +
         '<GetItem' +
               ' +
               xmlns_t="http://schemas.microsoft.com/exchange/
                  services/2006/types">' +
            '<ItemShape>' +
               '<t:BaseShape>IdOnly</t:BaseShape>' +
               '<t:AdditionalProperties>' +
                  '<t:FieldURI FieldURI="item:Attachments" /> ' +
               '</t:AdditionalProperties> ' +
            '</ItemShape>' +
            '<ItemIds>' +
               '<t:ItemId Id="' + itemId + '"/>' +
            '</ItemIds>' +
         '</GetItem>' +
      '</soap:Body>' +
   '</soap:Envelope>'; return soapToGetItemData;
}
Office.context.mailbox.makeEwsRequestAsync(soapToGetItemData,
   itemDataCallback);

Analyzing Attachments

Once the request is complete, we can use itemDataCallback to receive information on the attachment.

function buildAttachmentsInfo(xmlDoc) {
   var attachmentsInfo = "You have no any attachments.";
   if ($('HasAttachments', xmlDoc).length == 0) {
      return attachmentsInfo;
   }
   var attachSeparator =
      "---------------------------------------------";
   if ($('HasAttachments', xmlDoc)[0].textContent == "true") {
      attachmentsInfo = "";
      var childNodes = $('Attachments', xmlDoc)[0].childNodes;
      childNodes.forEach(function (fileAttachmentItem,
            fileAttachmentIndex) {
         fileAttachmentItem.childNodes.forEach(function
               (item, index) {
            if (item.tagName.includes("AttachmentId")) {
               attachmentsInfo = attachmentsInfo.concat
                  (item.tagName.replace("t:", "") + ': ' +
                  item.getAttribute("Id") + "<br>");
               return;
            }
            attachmentsInfo = attachmentsInfo.concat
               (item.tagName.replace("t:", "") + ': ' +
               item.textContent + "<br>");
         });
         attachmentsInfo = attachmentsInfo.concat(attachSeparator);
      });
   }
   return attachmentsInfo;
}

Adding Properties to E-mails

Here is an example on how to use JavaScript API to add some custom properties to the e-mail:

Office.context.mailbox.item.loadCustomPropertiesAsync(function
      (asyncResult) {
   var customProps = asyncResult.value;
   customProps.set("myProp", "value");
   customProps.saveAsync(function (asyncResult) {...});
});

Editing the Body of the E-mail

When all custom properties have been added, we can include the gathered information into the e-mail itself. To do this, we need to fully replace the whole text of the e-mail with the same one, but with additional info at the end. However, it is best not to use setSelectedDataAsync to receive the original text, because this function adds the position of the cursor. It’s better to use the setAsync function, for example:

Office.context.mailbox.item.body.getAsync("html",
      { asyncContext: "This is passed to the callback"},
      function (result) {
   var newText = result.value + "<br>" + attachmentsInfo;
   Office.context.mailbox.item.body.setAsync(newText,
      { coercionType: Office.CoercionType.Html },
      function (asyncResult) {...};
});

Sending an E-mail

Once the information has been added to the e-mail, we can send it by using a EWS request:

function getSendItemRequest(itemId, changeKey) {
   var soapSendItemRequest = '<?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_xsd="http://www.w3.org/2001/XMLSchema"' +
         xmlns_soap="http://schemas.xmlsoap.org/soap/envelope/"' +
         xmlns_t="http://schemas.microsoft.com/exchange/services/
            2006/types">' +
      '<soap:Header>'
         '<RequestServerVersion Version="Exchange2013"
             soap_mustUnderstand="0" +
      '</soap:Header>' +
      '<soap:Body> ' +
         '<SendItem ' +
               'SaveItemToFolder="true">' +
            '<ItemIds> ' +
               '<t:ItemId Id="' + itemId + '" ChangeKey="' +
                  '" /> ' +
            '</ItemIds> ' +
            '<m:SavedItemFolderId>' +
               '<t:DistinguishedFolderId Id="sentitems" />' +
            '</m:SavedItemFolderId>'
         '</SendItem> ' +
      '</soap:Body> ' +
      '</soap:Envelope> ';
   return soapSendItemRequest;
}
Office.context.mailbox.makeEwsRequestAsync(soapToGetItemData,
   function (asyncResult){...});

There are itemId and changeKey parameters that will be received by the function. To get an itemId, we need to save an e-mail. A changeKey is then received in response to the request. Thus, because we modified an e-mail, we need to save it again to get another changeKey.

...
Office.context.mailbox.item.saveAsync(function (result) {
   var soapToGetItemData = getItemDataRequest(result.value);
   Office.context.mailbox.makeEwsRequestAsync(soapToGetItemData,
         function (asyncResult) {
      if (asyncResult.error != null) {
         updateAndComplete("EWS Status: " +
               asyncResult.error.message);
            return;
      }
      var xmlDoc = getXMLDocParser(asyncResult.value);
      var changeKey = $('ItemId',
         xmlDoc)[0].getAttribute("ChangeKey");
      var soapToSendItem = getSendItemRequest(result.value,
         changeKey);
      Office.context.mailbox.makeEwsRequestAsync(soapToSendItem,
            function (asyncResult) {
         if (asyncResult.error != null) {
            statusUpdate("EWS Status: " +
                  asyncResult.error.message);
               return;
         }
         Office.context.mailbox.item.close();
         clickEvent.completed();
      });
   });
...

Now, if there are no problems, the modified e-mail should be sent correctly.

E-mail before sending
Figure 5: E-mail before sending

E-mail after delivery
Figure 6: E-mail after delivery

Pros and Cons of JavaScript vs COM

JavaScript plug-ins offer two key advantages as opposed to COM:

  • Add-ins can be published in the Office store. This allows the developer to easily get them in the hands of end users.
  • Plug-ins work on different platforms, including Windows, macOS, and iOS.

However, they also has their share of drawbacks, including:

  • Limited functionality in compose mode (when sending new e-mail)
  • Add-ins cannot run without an input from the user

There is no support for IMAP and POP, and plug-ins work only with Exchange Server 2013 or later. Many of these disadvantages can be circumvented by using COM plug-ins. These plug-ins can:

  • Work in the background without the need for user input
  • Have no limits on the accounts used
  • Can get the content of attachments when an e-mail is being sent

However, despite all that, COM plug-ins also have their share of drawbacks:

  • Only Support Windows systems for desktops
  • Not allowed to be distributed through existing official digital storefronts
  • Each version of Outlook requires a different plug-in

Finally, it is worth mentioning that, although it is impossible to get the content of the attached file in JavaScript and thus check it for viruses, or make your plug-in work in the background, such features can be added in future updates.

About the Author

Author Dennis Turpitka, CEO of the Apriorit, is an expert in Digital Security solution business design and development, Virtualization and Cloud Computing R&D projects, and establishment and management of Software Research direction. He is a successful entrepreneur, who organized several security start-ups.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read