Office 365 Add-Ins Dialog API released

In this article I will briefly touch on the new Dialog API, the fact that it works in Outlook and that you need to have the most recent version of Office 2016 to get this to work.

Introduction

In his recent article Simon Jaeger published information about the new Office 365 API “DialogAPI” functionality

http://simonjaeger.com/lets-have-a-dialog-about-the-dialog-api/

This is a really nice UX item added to the Office 365 Add-In suite of functionality. If I need to ask the user to interact with the application and/or make a decision, it is now available right there from within the Add-In.

Not just for Word

While Simon’s example was created in Word, using the following code it was really simple to test and see if it was available in Outlook as well.

It is !!

d1

I used my firebuglite Add-In example to quickly load up an existing Add-In and paste the code into the console window. Took 2 minutes to verify which was great.

        var dialogUrl = location.href

        // Display the dialog.
        Office.context.ui.displayDialogAsync(dialogUrl, { width: 15, height: 27, requireHTTPS: true }, function (asyncResult) {
            if (asyncResult.status !== Office.AsyncResultStatus.Succeeded) {
                // TODO: Handle error.
                return;
            }

            // Get the dialog and register event handlers.
            var dialog = asyncResult.value;
            dialog.addEventHandler(Microsoft.Office.WebExtension.EventType.DialogMessageReceived, function (asyncResult) {
                if (asyncResult.type !== Microsoft.Office.WebExtension.EventType.DialogMessageReceived) {
                    // TODO: Handle unknown message.
                    return;
                }

                // Parse the message.
                var data = JSON.parse(asyncResult.message);
                showNotification('Hello #Office365Dev', data.name);

                // TODO: Do something with the data.

                // We got our data, time to close the dialog.
                dialog.close();
            });
        });

In Simon’s post he also referenced the Channel 9 video created to go along with the announcement. In that the presenters demo how to use the new dialog API to Authenticate to O365 and return the token back to the Add-In. This is a more elegant method than what I have been using with the whole new IE window opening. I will be looking into combining the methods for my own nefarious use in the near future….. 🙂

Office Version (April 2016)

I wasted over a day being frustrated with why my code would not worked, and it turns out that my version of Office was not up to date. You need to go to the following site to download the latest version of Office (https://github.com/OfficeDev/Office-Add-in-Commands-Samples/blob/master/Tools/LatestOfficeBuild.md).

 

Conclusion

The new Dialog API presents a new option for developers in their continued ability to bring functionality into the Office 365 environment.

 

Advertisements

Creating an O365 Authorization Token via Chrome Extension

In this article I will show how I extended my method for generating an O365 OAuth token and incorporated it into a chrome extension.

Introduction

When I created the O365Token project on GitHub it was for the express purpose of being able to generate an OAuth token for an O365 Add-In project. I have since been tasked with integrating O365 into an application via a Chrome Extension. Using the same methodolgy it turned out to be relatively simple to do.

Creating a Chrome Extension

There is an extensive API for creating Chrome extensions which allows us to execute JavaScript at run time for a given website. What is particularly useful is that we are able to execute different scripts based on the site being accessed.

The basic extension

I created a extension locally with nothing more than the basic inclusion of a manifest and a JavaScript hello world.

g1

The background.js file is very simple

$(document).ready(function() {
    console.log('hello world');
})

and when I visit an https://xomino365.com site I see the extension log in the console.

g2

Adding O365Token to the extension

In the github project we have two files, Home.js and App.js. These two files are added to the chrome extension and the folder structure.

The Home.js file is modified with the necessary parameters for generating the OAuthToken as described in this previous article. I also modified the replyURL as in this case it is not an Office Add-In.

g3

You will also notice that I changed the first few lines of code to remove the call to Office.js.

Executing the Chrome extension

Once the code has been implemented, I go to a https://xomino365.com webpage and I am immediately prompted with a new window requesting O365 Authentication.

g4

g5

Once I authenticate, an OAuthToken is created and set as a cookie within the xomino365.com domain. Using this I can then add O365 application data into my xomino365.com website seamlessly.

I

Because of the ability to “match” the website which this occurs in, within my extension, this functionality will only appear on this website.

Updating the O365Token Github

I have updated the O365Token Github site with the Chrome extension as a separate folder within the repository. Please feel free to try it out for yourself with your own O365 sites.

 

Conclusion

In this article we have seen how a simple Chrome Extension is constructed and how we can add O365 Authorization capabilities to it.

The ability to easily integrate O365 through a Chrome Extension opens up the future possibility for integration between existing applications and O365 through the use of DOM insertion. More on this in a later article.

Corporate Tools to come…..

What is intriguing to me is that unless you’re using corporate GMail, Chrome itself is not normally a “default browser” inside a company using O365. More likely of interest to corporate world is that Microsoft Edge Extensions are coming (as of April 2016) and they are supposedly going to be very similar to Chrome extensions. This could very well have a lot more developer leverage in that environment. I guess I need to get on that train and find out……

 

 

Getting attachments from an O365 SharePoint list item Cross-Domain

In this article I will demonstrate the nuances of being able to get Office 365 SharePoint attachments in the same domain and in the Cross-Domain scenarios.

Getting information about a list item

When requesting information from a list in SharePoint, the detail returned does not by default contain any information about attachments other than “yep I got some”. The image below shows a typical response from a simple request to show the first value in the list

var path = "https://psclistensdev.sharepoint.com/sites/demo/";
$.ajax({
  url: path+"_api/web/lists/GetByTitle('Company')/Items?$top=1",
  type: "GET",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer " +  app.getCookie("OAuthToken")
  },
  contentType: "application/json;odata=verbose"
}).done(function(res){
    console.log(res)
})

s1

To be able to see information about the Attachments we add the $expand=AttachmentFiles parameter to the URL

var path = "https://psclistensdev.sharepoint.com/sites/demo/";
$.ajax({
  url: path+"_api/web/lists/GetByTitle('Company')/Items?$expand=AttachmentFiles&$top=1",
  type: "GET",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer " +  app.getCookie("OAuthToken")
  },
  contentType: "application/json;odata=verbose"
}).done(function(res){
    console.log(res)
})

s2

Looking at the Attachment information

There are multiple URLs which are returned in the JSON – looking at each of them we find that some open/download the file

  1. //psclistensdev.sharepoint.com/sites/demo/Lists/Company/Attachments/1/quickstart_guide.pdf
    • works great file downloads
  2. //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)
    • Creates a file called “AttachmentFiles(‘quickstart_guide.pdf’)” which when opened in notepad is actually an XML reference document…

So when downloading the file attachment in the same domain – requesting the file via the first URL – works just fine

s3

But if we move to a different domain (which has previously been added as a trusted App through Azure AD) we find that the Cross Domain headers are not provided and therefore the download fails…..

s7

I believe (although I cannot be sure) that this is because the direct URL for the attachment does not use the “_api” it is not being picked up by the Azure AD App permission process and the headers are not being added.

Doing it the other way

Going back to URL #2 (which if you remember returns an XML file)

  • //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)

If we try to request this, it does actually work…..but returns the JSON version of the XML file (cos I asked it to in the headers)

s5

The trick here is to add “$value” to the end of the URL. This triggers SharePoint to send out the attachment itself

  • //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)/$value

s6

Conclusion

In this article we have seen how there are multiple ways to collect the reference to a file attachment from a list item in O365 SharePoint. While both URLs work when requested from the same domain, only one of them triggers the Azure AD App registration model to add the appropriate HTTP Headers to the request, allowing a Cross-Domain request to be successfully executed.

 

 

 

 

 

 

Getting started locally with a basic O365 Word Add-In (no web hosting)

In this article I will demonstrate how to add a local shared drive to your Trust center which will facilitate developing basic Add-In functionality locally. This will not works for Add-Ins which require internet access (c:\local cannot call https) but for testing out your Word API skills this is perfect.

Introduction

Using the example set out on Word Add Ins (https://github.com/OfficeDev/office-js-docs/blob/master/word/word-add-ins.md) I was able to start playing with the Word JavaScript API locally. But at times I did not have a network share to map my add in with. With a little searching though I found that you can map a local file with \\localhost.

Taking from the example (which you should read first) I am going to modify the section about adding a trust center with an example.

My files are located at c:\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev

Adding the Trust Center

Within Word 2016 select

  • File
  • Options
  • Trust Center
  • Click the Trust Center Setting Button
  • Select the Trust Center Add-Ins option on the left

In the Catalog URL bar I add \\localhost\C$\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev and “Add Catalog”

w5

The Catalog is added as a network share

w6

make sure you check the “Show in Menu” option and restart Word

Using the example manifest

Using the example manifest provided in the Github Project (link again) and my own GUID I was able to create a manifest to my local files like this

 

<?xml version="1.0" encoding="UTF-8"?>
    <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="TaskPaneApp">
        <Id>0646c7a1-c876-482b-9119-e0f25c30b610</Id>
        <Version>1.0.0.0</Version>
        <ProviderName>Microsoft</ProviderName>
        <DefaultLocale>en-US</DefaultLocale>
        <DisplayName DefaultValue="Boilerplate content" />
        <Description DefaultValue="Insert boilerplate content into a Word document." />
        <Hosts>
            <Host Name="Document"/>
        </Hosts>
        <DefaultSettings>
            <SourceLocation DefaultValue="c:\Users\mroden\WebstormProjects\Azure\xomino365\localWordDev\App\Home\home.html" />
        </DefaultSettings>
        <Permissions>ReadWriteDocument</Permissions>
    </OfficeApp>

Using that I was then able to run the example code. There were a couple of warning though to accept to get this to work.

w1

w2

w3

There we have it – the locally hosted example working.

w4

Using the technique to add firebuglite to the Add-In I was then able to start to copy an paste example code from Microsoft examples and start to learn the Word API and its nuances.

I am also found this Office Snippets site which was GREAT to get started as well

http://officesnippetexplorer.azurewebsites.net/#/add-in/word

Conclusion

Being able to map a localhost as a “network drive” allowed me to get started without the need to constantly check code into my local Git repository and sync it with Azure

Productive !!!

Limitations of Internet Explorer when considering using Cookies with O365 Add-Ins

In this article I will demonstrate some of the limitations of Internet Explorer’s cookie handling and how we need to architect out Office Add-Ins to prevent overload thereof.

Introduction

In a previous article I demonstrated how we could cache data within an O365 Add-In using document.cookie. The logical extension of this would be to use it to cache OAuth tokens from Azure AD. In that way users would not have to re-authorize every time then opened the Add-In.

An OAuth token in the O365 environment is typically around 1500 characters in length !

c1

Blowing up cookies in Internet Explorer(11)

In Internet Explorer there is a finite limit to the total size of the cookies on any one page. Those cookies are built up of all the cookies valid for that path.

We can easily demonstrate how to blow this up by successively adding more and more data to a cookie as you can see below. As we add the same OAuth token over and over again we successively increase the length of the cookie until we cross 10,000 bytes and it blows up to zero.

c2

The implications of this are stark; if we cache an OAuth token for more than six O365 Add-Ins (even if they all get a different name) in the same path, we could destroy all of them without the user even knowing. This is not acceptable and we need a more elegant solution.

Cookie Pathing

When creating a cookie, the path is an optional variable and if it is not added the “path” it defaults to the current location.

;path=path (e.g., ‘/‘, ‘/mydir‘) If not specified, defaults to the current path of the current document location.” – From Mozilla’s documentation

Using smart deployment architecture of Add-Ins

The implication therefore it to ensure that all of the Add-Ins are hosted in different directories (if they are in fact all deployed on the same server). Without setting a path the cookie will be created with the path to the current location.href directory

In the following example we set a cookie in one directory, and it is unavailable in the second directory.

c3

c4

Conclusions

Although this is probably not a significant issue in the wild (because most people are not going to put all their Add-Ins in the same folder for deployment), it is something good to be aware of. There is a finite limit to the size of any single cookie in Internet Explorer, don’t blow it.

 

Office Add-In problem solved: Internet Explorer by default does not allow CORS

In this article I will highlight an issue you may come across when you are trying to deploy an Office Add-In pulling data from a different domain.

Introduction

As my adventure into Office Add-Ins has continued I began to look at integrating data from outside of the O365 environment, into Office Add-Ins. In this example specifically pulling data from an external source into an Excel spreadsheet.  I followed the example code from the Build your first Excel add-in site and as I showed in the Firebug Lite post I was able to take the example code and make it work in an Add-In. I then wanted to prove to myself that I could pull it from a real data source.

The Data Source

The data source was a Domino web service which I was able to duplicate the data feed from. As you can see from the response headers I am adding everything necessary to allow CORS. I was hosting the Add-In at napacloudapp.com and looking to pull the data from copper.xomino.com

d1

But every time I ran the code I had an error which told me that the data could not be pulled from copper. For the life of me I could not figure out how until I came across a post on stackoverflow (which I cant find to reference now) which pointed me in the direction of IE security settings.

By Default CORS is turned off in Internet Explorer !

This is a setting which goes WAY back to the start of Cross Site Scripting issues and way before CORS itself was even a standard. This was actually something Microsoft did right with old IE !

If you go to Tool – Internet options – Security and look inside the Custom Settings option for Internet sites you will find that accessing data across domains is Disabled! So CORS isn;t even an options – cross domain data retrieval is off.

d2

The solution – Trusted sites

You can change this for Internet sites in IE10 or IE11 because both of those browsers have CORS built in to protect you – not so much in IE9.

In a business environment though you should be using Trusted Sites. If your Add-In HOST website (www.napacloudapp.com in my case) is added to Trusted Sites then the Access data across domain options is set to “Enable” and the problem is fixed.

Conclusion

This was something I first encountered way way back over 10 years ago when I was messing with XML Islands back in IE5 ! I knew it was there but had never really though about it in this context.

Because the client based Add-In experience is basically an embedded Internet Explorer instance, all the browser settings are carried into the experience with it.

As is often the case, once you understand the problem, the solution is relatively simple – it’s just getting there which is bothersome 🙂

 

 

 

 

Caching data across disparate O365 Add-Ins (and across client types)

In this article I will show how data can be cached between O365 office Add-Ins. This also includes different clients (Outlook, Excel etc, even Internet Explorer)

Introduction 

In the previous article we looked at how we are able to cache data within an Office Add-In using the browser’s document.cookie capability. In this article I will demonstrate how flexible this capability is and how it will even work across different Add-Ins and different applications.

Creating a Cookie

Cookies can be created and be stored within a specific domain/path and this is critical for this example to work. If you do not add a path variable to the cookie when creating it, it will not be available outside of the specific URL from whence it was created.

Going back to the setCookie code I used before:

function setCookie(name, value, duration) { 
  var now = new Date();
  var time = now.getTime();
  var expireTime = time + 1000*duration;
  now.setTime(expireTime);
  document.cookie = name+'='+value+';expires='+now.toGMTString()+';path=/';
}

We can see that there is a path=/ variable added in the creation. This statement means that this cookie is available to all web pages within the domain. Without it, the cookie is tied to the exact URL it was created from. Ideally you would not use the whole domain and you would be more specific to a single directory path. For the sake of this example though it makes life easier 🙂

What this means for us though is that we are able to create a cookie from one Add-In (URL Host) and as long as it is in the same domain, we can access that cookie value in another.

Creating the cookie in one Add-in

In the example below we are setting the cookie in the Outlook Add-in. The location.href  (which is the host page for the Add-In) is https://copper.xomino.com/xomino/extjs.nsf/O365Token/index.html. You can see from the Firebug Lite console that Outlook has appended the client name to the end of the URL.

c6

Using the setCookie function we create the “EXAMPLEToken” cookie and make it valid for 120 seconds.

We then open Excel and look at an Add-In I created there. As you can see from the example below the location.href for the hosted Add-In file is https://copper.xomino.com/xomino/O365.nsf/index.html. While the path is not exactly the same as the Outlook example – the hosting domain is (copper.xomino.com).

c7

The example above uses a getCookie function to get the value of the “EXAMPLEToken” and our value “xomino365” is returned successfully.

After two minutes a subsequent attempt to get the cookie fails because it has expired

c8

And it is as simple as that.

Conclusion

Adding a path variable to the cookie creation has allowed us to create the cookie within on Add-In (Outlook in this case) and access it from within another Add-In (Excel in the second case).

Caveat

Be aware that you need to have control and consideration about how much data you load into a cookie. Any Internet Explorer cookie for a given domain cannot be more than 10234 chars.

Addendum

What is actually interesting to find out is that even though the embedded client Add-In used Internet Explorer – any cookies created within the client are actually available within the stand alone Internet Explorer – and Vice Versa!

The cookie created in outlook before – is available in full Internet Explorer as well.

c9

This has to raise some fascinating potential for a truly immersive experience between users in the outlook clients and their web browsing. Here it is being set in IE and being read in the Excel Add-In

c10