Many companies are working in a context where content sensitivity is very important. You should be very careful on what information you are sharing and with whom, especially if one or more recipients don't belong to your organization. To help employees being more diligent, the Outlook team developed a while ago a feature called MailTips, which is a way to provide contextual information to a user when a specific condition happens. One of the conditions that IT admins can turn on is external users checking. The image below shows an example of what happens in my Outlook client when I try to send a mail that includes a recipient who is outside Microsoft:
You can notice that, as soon as I add my personal mail address, the recipient is highlighted in yellow and I get a message at the top of the mail notifying me that there's one external recipient, with the option to quickly remove it. The MailTip, however, is just a warning. If I try to send this mail, I don't get further notifications and the mail is simply sent.
For some companies who are very sensitive to the confidentiality topic, this isn't enough. They need a more direct way to alert an employee when they're sharing a mail with an external user, to make sure that they aren't sharing any confidential information.
In this blog post, we're going to build an Outlook add-in that we can use to achieve this goal. The add-in is going to intercept the click on the Send button and, in case one or more recipients are outside the organization, will display a warning pop-up. The user will need to confirm a second time before actually sending the mail:
To set up the project, we will need to use the Yeoman generator to create an Office add-in using the web development model. Once you have installed all the required tools highlighted in the documentation, open a terminal, and create a folder where you want to store your project:
mkdir outlook-external-users
Then start Yeoman with the following command:
yo office
You're going to start a wizard to scaffold the starting project:
Now let Yeoman generate the project and restore all the required dependencies from NPM, the Node Package Manager.
The default template generated by Yeoman supports the two standard scenarios: task pane (the sidebar that is displayed on the left) and command (an operation that is directly executed on the current context, like a selected portion of the mail text). In our scenario, instead, we must support a third use case: event-based activation. Our add-in must stay "silent" in background and come alive when a specific action happens, in our case the user pressing the Send button on a mail compose window. The official documentation lists all the supported events that we can intercept. The one we are interested in is called OnMessageSend
.
The first change we must make is in the manifest file, called manifest.xml
and included in the root of the project. Inside the file, you will find a section called ExtensionPoint
, which sits under the DesktopFormFactor
section, with, as type, MessageReadCommandSurface
. This is the default extension point of the template, which means that the add-in buttons will be visible when you're reading an e-mail. We can ignore it for our scenario. Let's add, below this extension, another one with the following syntax:
<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnMessageSend" FunctionName="onMessageSendHandler" SendMode="PromptUser" />
</LaunchEvents>
<!-- Identifies the runtime to be used (also referenced by the Runtime element). -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ExtensionPoint>
We are creating a new ExtensionPoint
entry with type LaunchEvent
, which is our event-based activation scenario. Inside, we must specify a collection of LaunchEvent
entries, one for each event we want to listen to. In our scenario, it's only one event: OnMessageSend
.
The second parameter is equally important: FunctionName
is the name of the TypeScript function we're going to create to handle the event. The final property is called SendMode
and it's applied only to events which are related to a Send event, like in this case (another example is SendAppointment
). The PromptUser
value means that in case the condition we have defined is not met, the user will see a prompt that will give them the option to cancel the operation or send the mail anyway.
This is the correct behavior for our add-in, thus it's important to specify it, otherwise Outlook will switch to the default behavior, which is SoftBlock
. In this case, the pop-up will be displayed without any option to send the mail anyway, the user will be blocked until the condition is fixed. This isn't the behavior we want: the pop-up should be an extra layer of warning that external users are included, but we shouldn't completely prevent users from sending mails to people outside the organization!
The next step is to create a file that will host our event management code. The basic project already contains two folders for the two default scenarios (taskpane
and commands
), let's add a new one called launchevent
and, inside it, let's create a file called launchevent.js
.
Since this is a new JavaScript file which isn't included in the default template, we must manually reference it. Open the commands.html
file inside the commands
folder and, inside the <head>
section, add the following entry:
<script type="text/javascript" src="../launchevent/launchevent.js"></script>
The last step is to change the Webpack configuration. When you test or publish your add-in, Webpack takes care of bundling all the required assets together. Since now we have a new JavaScript file in our project, we must make sure it's properly included. Open the webpack.config.js
file in the root of the project and, inside the plugins section, add the following entry:
new CopyWebpackPlugin({
patterns: [
{
from: "./src/launchevent/launchevent.js",
to: "launchevent.js",
},
],
}),
That's it. Now we have all the building blocks which are needed to intercept the mail send. We just need the code to do it 😃
Let's look at the code that we need to include in the launchevent.js
file:
const customerDomain = "@contoso.com";
function onMessageSendHandler(event) {
let externalRecipients = [];
Office.context.mailbox.item.to.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
var recipients = asyncResult.value;
recipients.forEach((recipient) => {
if (!recipient.emailAddress.includes(customerDomain)) {
externalRecipients.push(recipient.emailAddress);
}
if (externalRecipients.length > 0) {
event.completed({
allowEvent: false,
errorMessage:
"The mail includes some external recipients, are you sure you want to send it?\n\n" +
externalRecipients.join("\n") +
"\n\nClick Send to send the mail anyway.",
});
} else {
event.completed({ allowEvent: true });
}
});
} else {
event.completed({ allowEvent: true });
}
});
}
// IMPORTANT: To ensure your add-in is supported in the Outlook client on Windows, remember to map the event handler name specified in the manifest's LaunchEvent element to its JavaScript counterpart.
// 1st parameter: FunctionName of LaunchEvent in the manifest; 2nd parameter: Its implementation in this .js file.
if (Office.context.platform === Office.PlatformType.PC || Office.context.platform == null) {
Office.actions.associate("onMessageSendHandler", onMessageSendHandler);
}
The core of the file is the onMessagesendHandler
function, which is registered as handler every time the Send button of the mail compose window is clicked. Let's see, step by step, the operations performed inside the handler:
Office.context.mailbox.item.to.getAsync()
. This is an asynchronous API, so we need to manage the result inside a callback.status
property of the result, we get the information if the operation has succeeded. If that's the case (Office.AsyncResultStatus.Succeeded
), we use the value
property, which is a collection of recipients.forEach()
statement to iterate through all the recipients.emailAddress
property, and we test if it includes the domain we consider internal (in this example, we check if it's a @Contoso.com mail address).externalRecipients
.event.completed()
, to notify Outlook that we have completed our checks, but we set the allowEvent
property to false
, which means that our condition isn't satisfied, thus we must display the pop-up. In this case, we populate also the errorMessage
parameter with the text that we want to display to the user, which includes the list of external recipients we have identified.event.completed()
as well but, in this case, we set the allowEvent
property to true
. The event can be executed, so we allow Outlook to send the mail without showing any prompt.This is the code implementation. We're ready to debug our add-in!
From the debug point of view, there's no difference between an event-based activation add-in and a traditional Outlook add-in. The easiest way to test it is by using Visual Studio Code. When you open your project and you move to the Debug tab, you will find multiple debug profiles, including one called Outlook Desktop (Edge Chromium):
Select it and press F5 (or click on the Play icon). Visual Studio Code will launch Outlook desktop and will sideload the add-in. Now compose a new e-mail and add, in the To field, at least one e-mail address which domain doesn't match with the one you have specified in the customerDomain
property. If you did everything correctly, you should see the same experience I've shared at the beginning of the post:
When you click on Send anyway, the compose window will be closed and the mail will be sent; if you click on Don't send, instead, the user will go back to the Compose window and they will be able to change the content of the mail or the list of recipients.
In this post, we have learned how to build an Outlook add-in which uses the event-based activation model to provide a more restrictive external recipients experience to users. Thanks to this add-in, we can add an extra layer of security to make sure that employees aren't sending confidential information to external users.
You can find the complete project on GitHub. Remember that this add-in uses the web model so, if you want to use it in your environment, you will have to host it on your own. For example, you can leverage Azure Storage by following the guidance on the official documentation.
Happy coding!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.