Building Office Add-ins using Office.js – the book, the website

I wanted to show some love for the new website and book by Michael Zlatkovsky. Michael is a Software Developer and Program Manager in the Microsoft Office Extensibility team. I met Michael at the MS MVP Summit back in October and we had a great conversation or two about Office Addins and their future development.

So Michael’s new leanpub book is called Building Office Add-Ins using Office.js and can be purchased through leanpub. The new supporting website http://buildingofficeaddins.com/ provides some of the key information from the book and gives a nice overview thereof while introducing the reader to the more technical aspects of using Office.js.

Michael’s a great guy and this is a very valuable resource for Office Add-In developers. I heartily recommend it.

Sharing data between Office Add-Ins using localStorage

In this article I will show how the underlying dependence on the browser (in this case IE11 in Windows) allows us to pass data between Office Add-Ins through the use of HTML5 localStorage. At this point this is a theoretical post as I haven’t thought of a good use case yet, I am sure I will at some point…

Introduction 

Earlier this year I created a demo for the Salesforce global conference (Dreamforce) which showed how to create an Office Add-In to extract and manipulate Salesforce data from within an Office Add-In (you can see the presentation here). For the main demo I actually created one “Web Page” and reused it in the context of Word and Excel. In this article I will demonstrate how we can easily set a localStorage value in IE11 or in an Office Add-In and have that readable in the other client environments.

localStorage

localStorage is part of the HTML5 Web Storage API which allows the permanent storage of string values in a browser. Like cookies the ability to read and write is tied to a domain. You can get/set values very easily by using the following notation:

  localStorage.setItem('name', 'Marky');
  localStorage.getItem('name'); //Marky

or even as simple as

  localStorage.name = 'Marky';
  localStorage.name; //Marky

The Embedded Browser 

When utilizing an Office Add-In within the Office client apps we are really using an embedded browser session within the context of the external client. In the case of Windows it is Internet Explorer. What this means though is that when we access a web domain, and set a localStorage value, that value is always available to us, when using Internet Explorer on that domain. Let me show you.

The Example

Here is my application running hosted on Azure in the xomino365.azurewebsites.net domain

ls1

Through the console I created a new value

  localStorage.Marky = "This value set in IE11";

and as you can see below that is now accessible from localStorage

ls2

If I now load up my Office Add-In within Word we can see (using the F12 tool) that  it is the same URL (same web page). Because it is in the Add-In “host_Info=Word” is added to the URL but other than that it is the basically same.

ls3

Looking at the localStorage value we can also see “marky”

ls4

We can set a value here in Word

ls5

and then open the Add-In in Excel and retrieve the localStorage values previously set

ls6

Caveat

If you have Word and Excel open you cannot set a localStorage value in one and have it picked up in the other, it seems that they only pick up the new values once they are opened. If you wanted to pass information between them in real time you could do that using WebSockets (something for a future demo).

Persistence between different Add-Ins

Now that I have set these values using the same application, we can also demonstrate that the principle is still applicable between different Add-Ins hosted in the same domain. In this example I have a different Add-In, still hosted on xomino365.azurewebsites.net (this time in Outlook). All the localStorage values are still available to me.

ls7

Conclusion

In this article we have seen how we are able to set a localStorage value in Internet Explorer and then Word and have those values available in Excel. We have also seen that these values are actually available in other Add-Ins as long as they are hosted on the same domain.

This capability is all due to the underlying fact that Internet Explorer is the browser used to create all the demonstrated functionality.

 

Using “F12” in Windows to get an Office Add-In development developer console

In this article I will demonstrate the ability to activate and use the Internet Explorer developer console from within an Office Add-In.

Introduction

As I have discussed before, without using the debugging tools available within Visual Studio it is hard to debug Office Add-In development. In the past I have used firebug lite to assist in this but it is far from ideal. Within Windows 10 there is a new capability to open a developer console which help alleviate this issue to an extent.

F12

On a Windows 10 operating system you will find F12 in the %system32% directory. Normally this would be c:\windows\system32\F12\F12chooser.exe

f1

 

Open up you favorite Office Add-In and or one you are working on and then open F12Chooser


f2

f3

 

Selecting TaskPane.html in this case will open the Internet Explorer developer tools for that web page. In Windows the Office Add-In is surfaced through an embedded IE windows within the office client.

f4

Using the developer console you can execute commands as normal

f5

You can also use the selector tool to look at styles

f6

 

Thanks to Michael Zlatkovsky (Microsoft Office Add-In dev – this guy) for showing me this 🙂

Conclusion

This is a major improvement for me over having to use firebuglite – interestingly though Michael was very intrigued that I had got that to work as well 🙂

 

Thank you – Microsoft MVP Summit !!!

Just over five weeks ago I was humbled to be awarded Microsoft MVP for the Office Development group and it has been a whirlwind few weeks. I was quickly enrolled in the Microsoft MVP Summit in Bellevue, WA and expectations were high. I now sit in Seattle Airport reflecting, glad to be going home but sad to leave…

Just like the first time I went to a major technology stack conference nearly 5 years ago, I didn’t know anyone and I didn’t know what I didn’t know. Professionally and personally this week exceeded my expectations. Not such much the technology, I kinda had a good idea about where that was going, but mostly the people, the company and the attitude. I got to go to Microsoft HQ, I got to see Building 1 and “Bill Lake”. Nerdy and kinda cool at the same time.

Technically, I can’t share what I learned – but that is not the point of this post…….sorry 😉

I wanted to say thank you to all the Microsoft employees for being so embracing, enabling and approachable. The summit was an excellent opportunity to share ideas, and more importantly discuss a vision for the future. I care less about what we do today and more about what we will be doing tomorrow and that is what I found. Plan for growth, go with a realistic vision, embrace tomorrow, take risks.

Due to rearrangements and program restructuring, there are only a handful of “Office Developer” MVPs and I am very excited to be in on the ground floor of something I believe in.

  • I learned that the Microsoft campus in Bellevue is one of the most beautiful business parks I have ever been to.
  • I learned that Seattle rush-hour traffic sucks just like anywhere else
  • I learned that the Microsoft product group I have met and worked with this week are aligned with my vision for their product and I am really excited it.
  • I have learned that what Microsoft is doing is complimentary to my IBM world, not competitive. It is perfectly OK to play in both worlds as a web developer.

Thank you to the poor souls who said no no…..”bitching is OK” and “it’s what we want”! I still don’t really believe you, and I still didn’t come hereto bitch, but you asked for it 😉 Thank you to all the product group people I met and I know I will forget some names (it’s only been a few hours)….I love what you do, I love what you did this week – THANK YOU to:

Rolando, Daniel, Michael, Yena, Sean, Michael, Gabriel, Sonia, Gareth, Sudhi, Caitlen and everyone I clearly forgot at Microsoft…..

Thank you to Lisa Anderson for corralling the Central MVPs and enabling me.

Thanks also to the many MVPs I met including David, Max, Leonid, Donald, Andrew Connell, Maarten, Colin and all the others !!!

I look forward to all seeing all you all again next year !

 

This coming year is going to be a lot of fun…..but then, that was the plan wasn’t it……….? 🙂

SharePoint now accessible via the Microsoft Graph beta

Last week at Ignite it was announced that finally Microsoft was bringing O365 SharePoint access to the Microsoft graph as an API. This is a huge deal for those of us who want to use O365 as a platform and develop engaging applications for customers. In my case for Office Add-ins this is great because it reduced the number of OAuth hoops I have to jump through and manage to get the data I want.

Here is a link to the documentation: Working with SharePoint sites in Microsoft Graph and a quick example.

NOTE: this is beta and will change – this is purely a demonstration of what is possible today (Oct 2016) and not to be used as future reference.

Example

Using the graph explorer demo I am able to bring up content from my default SharePoint site very easily

https://graph.microsoft.io/en-us/graph-explorer#

Here is my copper site within my SharePoint tenant

sh1

and here it is referenced from the graph API

https://graph.microsoft.com/beta/sharepoint/sites/site-id

sh2

here is the API response of the lists
https://graph.microsoft.com/beta/sharepoint/sites/site-id/lists

sh3

and here is a reference to the documents in the Shared Documents folder

https://graph.microsoft.com/beta/sharepoint/sites/site-id/lists/list-id/items

sh4

How cool is that !!!!!

 

Microsoft MVP for Office Development

This weekend I was thrilled to receive an email from Microsoft announcing that I had been recognized as a Microsoft Most Valuable Professional (MVP) in the Office Development technical community. This is a very natural evolution of my journey over the last two years since I branded out from nearly 20 years in IBM Domino. As I said about the xomino365 blog it was all about taking my skills on tour and seeing where it took me. While that blog served it’s purpose and I have come back to https://www.xomino.com exclusively, I am very content with the place it took me.

I wanted to take a moment to thank those people who nominated me (that I am aware of): Theo, John, Rob, Michael, Ren? and I am sure others – I really appreciate your support and belief in me.

I also wanted to thank Lisa Anderson (MVP wrangler for the mid-west) and Simon Jaeger (MS developer advocate) for their encouragement and support. They helped me know I was on the right path and are now responsible for what they helped create 😉

Finally I wanted to thank John Quirk and @PSCGroup who have given me a job and platform to allow me to do what I do best. I really love my job and the people I work with.

This (as always) is going to be fun 🙂

 

Thanks again

 

Speaking at Dreamforce next week :)

Next week I will be in San Fransisco for the Dreamforce conference – this will be the biggest conference I have attended, never mind spoken at and I am really excited to go 🙂

If anyone is going to be around at the conference or in the area – ping me on Twitter @MarkyRoden – be good to meet up 🙂

 

Unleashing the power with Salesforce and Microsoft Office 365 Add-ins

https://success.salesforce.com/MyAgenda?eventId=a1Q3000000qQOd9EAG#/session/a2q3A000000LBjBQAW

TIME & LOCATION

Moscone West, Developer Lightning Theater

THANK YOU NOTES IN 9 !!!!!!!!!!!!!

Once upon a time there was a guy who worked for a trucking company and he decided that he needed to learn this new XPages thing – and he figured why not share what I am doing? That was over 7 years ago and it grew into the largest single video repository in the history of IBM Domino. Dave Leedy is coming to the end of his XPages-casting career and is hanging up his cleats in this arena. Not only did Notesin9 lead to more opportunities for Dave, it also helped many many other people.

Dave will not admit it, but http://www.Notesin9.com site has helped more people be successful that he could have ever imagined,

Dave and Notesin9 are a huge reason for my personal success in the last few years and I know I am not alone. Dave’s encouragement and the hosting costs he provided out of his own pocket provided a platform for my success unmatched in my professional career. I owe him a huge debt of thanks personally and many others do too.

So a few friends (44 people!!) and I got together and decided it would be cool to create something memorable that Dave could relate to and I really hope it gives you some idea of the love……

So today we are releasing the unofficial-official #thanksnotesin9 video. I hope you enjoy it. Share it, trend it, it’s worth it to everyone in the IBM Domino community.

I know there are many others who would like to contribute but we had to keep this on the quiet and had to be completed eventually. If you have a thanks video you want to make then link it in the comments below. The more the merrier 🙂

Cheers,

Marky

 

PS

make sure you watch it to the end 😉

Saving a word document directly from an Office Add-In into Salesforce.com

In this article I will demonstrate and discuss how to programmatically save a Word document directly into Salesforce.

Introduction

In previous articles we have looked at how to programmatically interact with a Word document from within an Office Add-In. In this case we will use the Office JavaScript API to access the file as a binary string and then convert it into a format compatible with Salesforce.com.

Turning your word document into a binary

This article in the Office dev center details how to Get the whole document from an add-in for PowerPoint or Word. We will use the bulk of that article up to the point where we send the slice. We are going to look at a modified sendSlice() function. The dev.office.com article details how you can access the binary contents of the word document, but falls short when it comes to getting the base64 encoded version of the file to be submitted to an external site. This is the complicated part !

Getting the right format for Salesforce

Unfortunately the salesforce documentation is not based around using JavaScript to access the REST APIs. It is based around using curl commands in UNIX. The sample page for Inserting or updating Blob Data explains how to upload a multipart message which includes both the meta-data values and the binary data for the file. For the record they also neglect to provide any guidance on how to get the binary of the file….!

Using the code posted on this stackoverflow post  we are able to turn a binary string into the necessary format to send the file to salesforce.

The final sendSlice() function is shown below. The significant thing to note is that the initial file must be sent to the ContentVersion sobject. Because this is a new document a parentId is not necessary and a ContentDocumentId is created. In a future article we will look at how to then associate this new file attachment is an existing object.

function sendSlice(slice, state) {
  updateStatus("In sendSlice");
  var data = slice.data;

  // If the slice contains data, create an HTTP request.
  if (data) {

    // Encode the slice data, a byte array, as a Base64 string.
    // NOTE: The implementation of myEncodeBase64(input) function isn't
    // included with this example. For information about Base64 encoding with
    // JavaScript, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding.

    var u8 = new Uint8Array(data);
    var b64encoded = btoa(String.fromCharCode.apply(null, u8));

    var objectData = {
      "Description": "Demo Upload Directly from Word",
      "Title": "sample.docx",
      "ReasonForChange": "Initial Upload",
      "PathOnClient": "sample.docx"
    }

    var boundary = 'boundary_string_' + Date.now().toString();
    var attachmentContentType = 'application/octet-stream';

    // Serialize the object, excluding the body, which will be placed in the second partition of the multipart/form-data request
    var serializedObject = JSON.stringify(objectData);

    var requestBodyBeginning = '--' + boundary
        + '\r\n'
        + 'Content-Disposition: form-data; name="entity_attachment";'
        + '\r\n'
        + 'Content-Type: application/json'
        + '\r\n\r\n'
        + serializedObject
        + '\r\n\r\n' +
        '--' + boundary
        + '\r\n'
        + 'Content-Type: ' + attachmentContentType
        + '\r\n'
        + 'Content-Disposition: form-data; name="VersionData"; filename="filler"'
        + '\r\n\r\n';

    var requestBodyEnd =
        '\r\n\r\n'
        + '--' + boundary + '--';

    // The atob function will decode a base64-encoded string into a new string with a character for each byte of the binary data.
    var byteCharacters = window.atob(b64encoded);

    // Each character's code point (charCode) will be the value of the byte.
    // We can create an array of byte values by applying .charCodeAt for each character in the string.
    var byteNumbers = new Array(byteCharacters.length);

    for (var i = 0; i < byteCharacters.length; i++) {
      byteNumbers[i] = byteCharacters.charCodeAt(i);
    }

    // Convert into a real typed byte array. (Represents an array of 8-bit unsigned integers)
    var byteArray = new Uint8Array(byteNumbers);

    var totalRequestSize = requestBodyBeginning.length + byteArray.byteLength + requestBodyEnd.length;

    var uint8array = new Uint8Array(totalRequestSize);
    var i;

    // Append the beginning of the request
    for (i = 0; i < requestBodyBeginning.length; i++) {
      uint8array[i] = requestBodyBeginning.charCodeAt(i) & 0xff;
    }

    // Append the binary attachment
    for (var j = 0; j < byteArray.byteLength; i++, j++) {
      uint8array[i] = byteArray[j];
    }

    // Append the end of the request
    for (var j = 0; j < requestBodyEnd.length; i++, j++) {
      uint8array[i] = requestBodyEnd.charCodeAt(j) & 0xff;
    }

    //Create the new file in Salesforce
    var url = "https://yourdomain.my.salesforce.com/services/data/v37.0/sobjects/ContentVersion/"

    return $.ajax({
      method: "POST",
      url: url,
      processData: false,
      data: uint8array.buffer,
      headers: {
        "Content-Type": 'multipart/form-data' + "; boundary=\"" + boundary + "\"",
        "Authorization": "Bearer " + app.getCookie(app.addinName)
      }
    }).error(function (data) {
      updateStatus("FAIL: " + JSON.stringify(data))
    }).success(function (data) {
      updateStatus("Success creating ContentVersion: " + data.id)
      closeFile(state)
    });

  }
}

 

Conclusion

In this article we have seen an example of how we can extend the functionality of an Office Add-In to access the binary data of a word document and then save it to a cloud provider (in this case Salesforce).