Hi friends, finally time for some posts with some real code samples, and not some silly scripts. In this post, and a couple of follow up posts, I will walk you through the basics behind the WOPI protocol and WOPI Apps and WOPI Hosts. In the end you will see how we can create our own viewers and editors for files just like the WAC Server 2013 can view and edit Microsoft Office files in SharePoint 2013.
What the heck is this WOPI stuff you are talking about?
Before we look into all my beautiful code, let’s go through some of the basics about the new WOPI protocol. WOPI stands for Web Application Open Interface and is a protocol created and published by Microsoft to allow direct file communication between a WOPI App and a WOPI Host. You can find the details in the [MS-WOPI] specification. Well, it’s not much details yet, since it’s a working draft currently (version 0.2), but give it some time, or keep on reading these posts. Unfortunately since the specification is not yet done, things in this post might change in the future.
This is taken directly from the specification:
One example of how a client might use WOPI is by providing a browser-based viewer for a specific type of file. That client uses WOPI to get the contents of the file in order to present that content to the user as a web page in a browser. [MS-WOPI 1.3]
I think it’s more easier to explain by using an example. For instance, take SharePoint 2013 and Office Web App Server 2013 (aka WAC Server). SharePoint 2013 is a WOPI Host (WOPI Server in the specification), it can use WAC Server, the WOPI App (WOPI Client in the specification), to allow the users to view and edit files. The end user will initiate a request to view a document from the browser to the WOPI Host, the WOPI Host will query the WOPI Client for it’s capabilities (this process is called discovery). The user will then ask the WOPI Client to show the file, The WOPI Client will talk to the WOPI Host (hosting the actual documents) and get information about the file and eventually the actual contents of the file, and then finally render it so that the client can see it.
This whole process involves quite a few HTTP(S) round trips and all requests are signed and verified to make the process secure. WOPI is a protocol built on top of HTTP/HTTPS and the only allowed ports are 80/HTTP and 443/SSL. I recommend you to always use SSL for any kind of production environment, even though I in these posts use HTTP/80 – but that’s just to make it easier to debug using Fiddler.
So what are we going to build…
The WAC Server is a great and scalable implementation that allows users to work with Microsoft Office documents. But what about other document types, shouldn’t we have the opportunity to enjoy in-browser viewing and editing of those? Well, that’s exactly what this blog post series is about. I see this as a huge area where ISV’s can plug into the SharePoint world and build native viewers and editors. For instance, imagine if Autodesk took their web base AutoCAD editor AutoCAD WS and made that into a WOPI Client. All companies in the construction industry would dance in joy!
But what about us developers, don’t you all store your C# files in SharePoint, and how would it be with a nice C# viewer and editor in SharePoint. Well, that can be fixed. Follow along and let’s build it.
But, this ain’t for the cloud! You can only do these kind of amazing thing on real tin!
The WOPI Client outline
First of all, to build our WOPI Client we need to create a web site, hosted on port 80 or 443. This website will contain the WOPI discovery mechanism and it will have a ASP.NET forms page which acts as the viewer. In this sample we’ll make it a bit simpler for us and only allow HTTP/80 access to the client.
Setting up the project
To start with I create a new Visual Studio 2012 project using the Empty ASP.NET project template. This project is configured to be hosted in IIS for which I use a dedicated FQDN host header. This way it’s easier to host it on the same server as SharePoint during development.
Implementing WOPI Discovery
The first thing we need to do is to implement the WOPI Discovery mechanism. This mechanism is used by SharePoint 2013 (the Host) when you use the New-SPWOPIBinding cmdlet to connect to your client. That cmdlet will discover all features supported by the WOPI Client and depending on what parameters you give to the cmdlet it will register these bindings with SharePoint.
WOPI Discovery is implemented by making sure that your WOPI client responds to http://server/hosting/discovery. The client must return a data structure that describes the capabilities of the WOPI Client. This can be done using numerous methods. Here we’ll do a static implementation and just add a couple of folders and an XML file to the project.
Since we want it to respond to /hosting/discovery we just update the web.config so that discovery.xml is a default document, like this:
<configuration> ... <system.webServer> <defaultDocument enabled="true"> <files> <add value="discovery.xml"/> </files> </defaultDocument> </system.webServer> </configuration>
Now it’s time to actually define the WOPI operations in the discovery.xml file created. This is how the discovery must look like for our C# viewer:
<?xml version="1.0" encoding="utf-8"?> <wopi-discovery> <net-zone name="internal-http"> <app name="CSharp" favIconUrl="http://wacky.corp.local/images/csharp.ico" checkLicense="false"> <action name="view" ext="cs" default="true" urlsrc="http://wacky.corp.local/csharp/viewer.aspx?<theme=THEME_ID&>" /> </app> </net-zone> <proof-key oldvalue="" value="BwIAA...1w=" /> </wopi-discovery>
The first we need to define in the discovery XML is the net-zone which specifies the intended network type. The name must match the zone you have configured SharePoint 2013 to use. Look it up by using the Get-SPWOPIZone cmdlet.
Note: in a real world scenario you would build this XML dynamically and generate a net-zone block for each zone you would like to support. There must be at least one net-zone element and max four of them.
In each net-zone you will define a set of app elements. An app has a name, an icon and optionally a requirement for a license check. Note that I’m hardcoding the URL’s here, you would never do that of course and generate this XML dynamically instead. In each app element the operations that app supports are specified using action elements. Each action has a name, that name must be one of the following (the WOPI specification is a bit kinder though and forces the WOPI servers to accept any values): view, edit, mobileview, present, presentservice, attendservice, attend, editnew, imagepreview, interactivepreview, formsubmit, formedit and rest. We use view here, since we’re building a viewer (to start with). For the usage of each name, please look into the WOPI Specification, or follow along in this blog post series. The ext attribute specifies the extension of the files, default specifies in this case that this is the default action for this app. Finally we have the urlsrc attribute. That attribute represents the URL to be used by the WOPI Host when using this action on the app. The URL has a set of replaceable parameters that can be used. In this URL I’ve specified THEME_ID to be able to have a light or dark theme on the viewer. There’s a few more parameters to use.
[Updated 2012-09-30] Finally in this WOPI discovery XML we have the proof-key element. In the snippet above I’ve cropped the value attribute since it’s normally quite a long base64 encoded string. This element is so far not documented in the specifications but the value corresponds to a certificate key which is used to sign and verify responses from the WOPI client, to make sure no one tampered with the data. Typically you would like this value to be created when you install your WOPI client, but here’s the way to generate a static value for this demo (in PowerShell). Also this key should be re-generated at regular intervals, and therefore the oldvalue attribute. You’ll also see how this key is used in the next blog post.
$crypt = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 2048 $proof = [System.Convert]::ToBase64String($crypt.ExportCspBlob($false)) $proofwithkey = [System.Convert]::ToBase64String($crypt.ExportCspBlob($true)) $proof $proofwithkey
Take the value from the $proof variable and add to the proof-key value attribute in the discovery.xml, and save the value from $proofwithkey – you’ll need that in the upcoming posts. Simply explained the proof-key sent from the client to the server during the discovery process allows the server to verify all signed requests from the client.
That’s it, the WOPI Discovery implementation is done.
Register the WOPI Client with the SharePoint 2013
Even though we have not created our view yet, we can verify that the discovery works by trying to connect the WOPI client to SharePoint (our WOPI Host). This is done by using the New-SPWOPIBinding cmdlet, like this:
New-SPWOPIBinding -ServerName wacky.corp.local -AllowHTTP
Two things to notice here, I must specify the FQDN of the server name, without any ports (remember only 80 and 443 is allowed) and by default it tries to use HTTPS. To allow HTTP communication I need to add the AllowHTTP parameter. Once you run this command it will return all available WOPI Bindings. Note that any errors in the discovery definition will be reported as invalid XML exceptions. If you have not connected SharePoint to a WAC Farm, then this should be your only binding and it should look like this.
If it looks like above, then your golden!.
In case you want to remove your WOPI bindings you just use the Remove-SPWOPIBinding cmdlet like this:
Remove-SPWOPIBinding -Server wacky.corp.local
Some final fixes before we test it…
In the discovery XML we specified an icon and a viewer page. Let’s add them. I borrowed the icon from the Visual Studio templates and added that to an /images folder and I added an empty Web Form to the /csharp folder. Just add some random text to it so you know it’s your file. The project looks like this after the additions:
Now let’s fire up SharePoint, add a CS file to a document library. Then click on the file and the WOPI Client should be invoked and your viewer should be shown. Notice the icon and that the title of the page is the name of your file.
If you receive the standard Download File dialog when clicking the file after you’ve done your WOPI Binding then execute an IISRESET and wait a minute or two, SharePoint needs to recover from all you awesomeness!
In this post we’ve gone through the basics in how WOPI Servers and WOPI Clients works as well as started our implementation of our very own WOPI Client. You’ve seen how the WOPI discovery process works and how to register the WOPI Client with SharePoint 2013.
In the next part I’ll show you how to actually create the viewer by reading the file from the WOPI Server following the WOPI protocol standard.