Building add-ins with commands
Today we bring you the second in a two-part series of guest blog posts from Andrew Salamatov, a Senior Program Manager with our Outlook team. You can find the first part here. Enjoy!
Because of their advantages, we’re encouraging all developers building add-ins to use commands. In fact, soon we’ll start requiring add-ins submitted to the Office Store to use them for scenarios where it makes sense. In this post, I wanted to share the easiest way to get started building an add-in with commands.
To begin, we’ll need an Outlook 2016 client connected to an Office 365 account, a developer preview Outlook.com account, or an on-premises Exchange email account. As we announced back in April, this feature is new to Outlook 2016, which is shipping very soon (and other Outlook clients like mobile and web to follow soon!). For now, you can get your hands on an Outlook 2016 Preview client here. As for the email account, the easiest thing to do is to either use your existing Office 365 account or create one here.
Once you’ve installed the client, let’s get started building the add-in. In this blog post, I’ll show how to build an add-in with a simple button in a message compose form, which will translate selected text into Russian. The user flow will be very simple – user types in some text, clicks the button, and the text is replaced with its translation. As you know, add-ins consist of an XML manifest and html/javascript code. Let’s look at the manifest first.
The manifest
A quick aside on the manifest...
To support add-in commands, we had to introduce some changes to the existing manifest. To declare commands, developers use the existing 1.1 manifest, but must add a new section at the end called VersionOverrides
. We introduced this section for backwards compatibility. Older clients, which do not support add-in commands will ignore this and activate the add-in according to the definition above this element. Newer clients will parse and use this section to run the add-in, ignoring activation rules above VersionOverrides
. Because we still have users on older bits, your add-in must work the older way as well.
The concept behind VersionOverrides
is that elements defined inside it override ones defined above. You can refer to this list for all the elements that can be overridden.
You’ll notice that we’ve changed the Host
element to now contain extension points. Extension points are a new concept – the set of all extension points in a form factor define all the different ways that the add-in will integrate in that form factor. In the future, we will add new extension points as well as support for more form factors.
Ok, back to the translation add-in. The key part of the manifest is the Host
element:
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<FunctionFile resid="functionFile" />
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="translateGroup">
<Label resid="groupLabel" />
<Tooltip resid="groupTooltip" />
<Control xsi:type="Button" id="translateButton">
<Label resid="translateButtonLabel" />
<Tooltip resid="translateButtonTooltip" />
<Supertip>
<Title resid="translateSuperTipTitle" />
<Description resid="translateSuperTipDescription" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon1_16x16" />
<bt:Image size="32" resid="icon1_32x32" />
<bt:Image size="80" resid="icon1_80x80" />
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>translate</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
You’ll notice here that we’ve declared an extension point MessageComposeCommandSurface
, and in it we have defined controls. This generic name creates an abstraction, so developers have a single way to declare a button on a primary command surface and have it show up across Outlook desktop, Outlook.com and Outlook Web App (support for the latter two coming very soon). Take a look here to see which other extension points are supported currently.
The MessageComposeCommandSurface
is made up of OfficeTab
elements. These correspond to ribbon tabs in Outlook. Developers can declare one group on the home tab and/or one custom tab with up to ten groups (and six buttons per group). For the translate add-in, we’ll utilize the default Outlook tab (if you wish to create a custom tab, use the CustomTab
element instead).
Next, take a look at the Control
element. It’s a button, whose action is to execute a function. Most of the elements in it should be self-explanatory, but take a look here for complete documentation. What’s interesting about this button is its action. Rather than showing a task pane or a dropdown, Outlook will execute the specified function in a browser instance. Of course, that JavaScript function will have access to the entire Office.js API as well as all other JavaScript APIs. So, for example, if it needs to launch a pop-up browser window, it can do so using window.open()
. This function must be defined in the HTML file specified by the FunctionFile
element at the top of VersionOverrides
(see full manifest in our Git repo linked at the bottom).
Finally, you’ll notice several resid
attributes. We’ve made a slight change how strings are specified in the manifest with the VersionOverrides
update. Since for some buttons, developers will want to reuse the same string, which might have many locale overrides, all strings and URLs are now defined once in separate section, and referenced via a resource id, or resid
. This section is at the end of the manifest:
<Resources>
<bt:Images>
<bt:Image id="icon1_16x16" DefaultValue="https://ssl.microsofttranslator.com/static/222083/img/trans.png"/>
<bt:Image id="icon1_32x32" DefaultValue="https://ssl.microsofttranslator.com/static/222083/img/trans.png"/>
<bt:Image id="icon1_80x80" DefaultValue="https://ssl.microsofttranslator.com/static/222083/img/trans.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="functionFile" DefaultValue="~remoteAppUrl/AppCompose/FunctionFile/Home.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="groupLabel" DefaultValue="Translator"/>
<bt:String id="translateButtonLabel" DefaultValue="Translate"/>
<bt:String id="translateSuperTipTitle" DefaultValue="Click this to translate text from English to Russian."/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="groupTooltip" DefaultValue="Translate actions"/>
<bt:String id="translateButtonTooltip" DefaultValue="Translates text from English to Russian."/>
<bt:String id="translateSuperTipDescription" DefaultValue="Translates text from English to Russian."/>
</bt:LongStrings>
</Resources>
The business logic
Now that we’ve written the manifest, we just need to hook up the logic to the button. Above, we defined the function which should be invoked when the button is clicked, and the file where the function is specified. As you’ll see in the complete sample solution, I broke out the logic into a separate JavaScript file, which is referenced by the HTML file. The main reason for this is to demonstrate that you can still have entirely separate js files, if you like. In the JavaScript file, you can see Office.initialize
and the function translate
. Office.initialize
gets invoked as usual first. You can do any authentication that you need here. Once that returns, the specified function in the manifest will be invoked. In our case, that’s translate
.
Important aside: The type of button we defined doesn’t show any UI natively, once pressed. This means that the user won’t know by default whether the click was successful, still processing or resulted in an error. The good news is that developers can use notification message APIs to convey progress and ultimately success or failure.
There are three types of notification messages:
- Progress indicator
- Informational message
- Error
Developers should always show a success or error message, as otherwise users may not know if the operation fully succeeded, or just partially.
Informational messages can be permanent, meaning every time the user clicks on this message/appointment the message will be there (unless the user dismisses it), or temporary, meaning that it will be shown only once to the user. An example of a permanent message would be "This email is now tracked in CRM." While a temporary example would be "Translation succeeded". The latter is not important for the user to see if he or she ever comes back to this message in the future.
Error messages are always displayed to the user just once, and developers don’t control this.
When it comes to progress indicators, it’s up to the developer whether or not to show one. If the function will almost always execute very quickly and return a result, showing a progress indicator for a few milliseconds will result in a "flash" to the user, which they weren’t able to see anyway. So in these cases it doesn’t make sense to show a progress indicator. On the other hand, if the button will make a call to a web service, which is known to take a bit, then a progress indicator should definitely be shown.
Note that if for some reason it takes us more than 500 ms to launch the function (maybe it’s taking us a while to load the HTML/JS), we’ll display a default progress indicator anyway. If the developer then invokes a method to add a new notification message of any type, the current indicator will just be replaced.
There are APIs to replace messages, so that the UI is as smooth as possible. For all the details, go here.
Back to the translate
function, which is where all of our logic happens (you’ll notice an event parameter passed – see documentation here and here). In this case, the service we’re using is pretty fast, so we won’t be adding a progress indicator. Instead, we’ll do the translation, and then set a success message (if we did show a progress indicator, we would call replace
, instead of add here). The last step is to call event.completed()
. This is critical, because it lets Outlook know that your function is done. Otherwise, there will be awkward UI that the user will see. The entire method looks like this:
function translate(event) {
Office.context.mailbox.item.getSelectedDataAsync("text", function (ar) {
var requestUrl = generateRequestUrl(ar.value.data);
$.ajax({
url: requestUrl,
jsonp: "callback",
dataType: "jsonp",
success: function (response) {
var translatedText = response.text;
var textToWrite = "";
for (var i = 0; i < translatedText.length; i++)
textToWrite += translatedText[i] + "<br/>";
Office.context.mailbox.item.setSelectedDataAsync(textToWrite, { coercionType: "html" }, function (asyncResult) {
Office.context.mailbox.item.notificationMessages.addAsync("success", {
type: "informationalMessage",
icon: "icon1_16x16",
message: "Translated successfully",
persistent: false
});
event.completed();
});
}
});
});
}
And that’s it! Now we can publish our code to a web server, install the manifest into a mailbox and try out the add-in. As always, check out Outlook Dev Center for more info. You can get help by posting a question on Stack Overflow, or on our MSDN forum.
You can find the full source code to the project on GitHub.
Enjoy!
-Andrew
Comments
Anonymous
October 25, 2015
megustaria velas instrucciónes pero en españolAnonymous
November 10, 2015
The comment has been removedAnonymous
March 18, 2016
Any information when add-in commands will reach other hosts, like OWA and maybe Outlook 2013?Can I have two (or more) add-ins share the same ribbon group for their buttons?- Anonymous
March 29, 2016
Commands are already coming to Outlook 2013, the update that enables it should be on Windows Update. We're working on it for OWA, but I don't have a timeline to share right now. Stay tuned to our blog or dev.outlook.com for updates!
- Anonymous
Anonymous
July 28, 2016
How we can show TaskPane (opening on right side) for read mail just like compose in Web OWA. This inconsistency is a disappointment. I can do this with add-in commands in Desktop Outlook 2013 & 2016 but not in web outlook. I saw Evernote is doing this on web, even they place the icon next to reply all (not at default location). If you hint me how Evernote might be doing it will be great.- Anonymous
August 05, 2016
Yes, I want to know it too! I can't find any information how Evernote and Wunderlist show their add-ins in vertical pane in Outlook online. - Anonymous
November 09, 2016
Same here. I also want to know how Ever note is doing this.
- Anonymous
Anonymous
November 24, 2017
Hi,I have created an addin for outlook client 2013 sp 1. The Main objective of the Add-in is to create a button on the home page ribbon of outlook client and when clicked upon, should launch a browser and redirect to an Intranet URL.In the Manifest file, i have tried various values for "ExtesnsionPoint". After deploying the manifest file through EAC, the addin shows up only on the read or compose message ribbons. How do i make the Button appear on the Home page ribbon. Kindly help.- Anonymous
November 27, 2017
Add-ins only activate in the context of a selected item as you've discovered. You need to have an item selected in order for your button to appear. If you're add-in does not make sense in the context of an item, you may want to look at module extensions.
- Anonymous