Can I just use Office Web Apps Server
Asked Answered
T

1

6

I am trying to have a document management system in browser with office. I have found this solution http://www.edrawsoft.com/officeviewer.php but it uses the client copy of office.

I would like to use Office Web Apps but my question is ... do I need to use it through SharePoint or other Microsoft products or can I just hook up a website to use Office Web Apps in browser for my own document system

Teter answered 12/6, 2013 at 12:3 Comment(0)
F
12

You can write your own server that implements WOPI protocol, this will support PPTX/XSLX in view/edit mode, DOCX/PDF in view mode only. WOPI server is pretty simple to implement.

To edit word docs you need to implement Cobalt or FSSHTTP/FSSHTTPB protocol.

Also don't forget licensing, Office Web Apps requires that all users have a valid office license.

Here is a sample .NET WOPI server:

Call it with:

http://OFFICEWEBAPPS.HOST/p/PowerPointFrame.aspx?PowerPointView=EditView&access_token=12345&WOPISrc=URLENCODED_URL_OF_THE_WOPI_SERVER

Like: http://WOPISERVER.HOST:2000/wopi/files/1.pptx

This will open 1.pptx in your c:\temp

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.IO;
using System.Runtime.Serialization.Json;

namespace WopiServerTutorial
{
    public class WopiServer
    {
        private HttpListener Listener;

        static void Main(string[] args)
        {
            WopiServer s = new WopiServer();
            s.Start();

            Console.WriteLine("A simple wopi webserver. Press a key to quit.");
            Console.ReadKey();

            s.Stop();
        }

        public void Start()
        {
            Listener = new HttpListener();
            Listener.Prefixes.Add(@"http://+:8080/");
            Listener.Start();
            Listener.BeginGetContext(ProcessRequest, Listener);
            Console.WriteLine(@"WopiServer Started");
        }

        public void Stop()
        {
            Listener.Stop();
        }

        private void ProcessRequest(IAsyncResult result)
        {
            HttpListener listener = (HttpListener)result.AsyncState;
            HttpListenerContext context = listener.EndGetContext(result);

            Console.WriteLine(@"Got a " + context.Request.HttpMethod  + " request for URL: " + context.Request.Url.PathAndQuery);
            var stringarr = context.Request.Url.AbsolutePath.Split('/');
            var rootDir = @"C:\\temp\\";

            if (stringarr.Length == 5 && context.Request.HttpMethod.Equals(@"GET"))
            {
                Console.WriteLine(@"Getting content for the file: " + rootDir + stringarr[3]);

                // get file's content
                var file = rootDir + stringarr[3];
                var stream = new FileStream(file, FileMode.Open);
                var fi = new FileInfo(file);

                context.Response.ContentType = @"application/octet-stream";
                context.Response.ContentLength64 = fi.Length;
                stream.CopyTo(context.Response.OutputStream);
                context.Response.Close();
            }
            //else if (stringarr.Length == 5 && context.Request.HttpMethod.Equals(@"POST"))
            //{
            //    // write
            //}
            else if (stringarr.Length == 4 && context.Request.HttpMethod.Equals(@"GET"))
            {
                Console.WriteLine(@"Getting metdata for the file: " + rootDir + stringarr[3]);
                var fi = new FileInfo(rootDir + stringarr[3]);

                CheckFileInfo cfi = new CheckFileInfo();
                cfi.AllowExternalMarketplace = false;
                cfi.BaseFileName = fi.Name;
                cfi.BreadcrumbBrandName = "";
                cfi.BreadcrumbBrandUrl = "";
                cfi.BreadcrumbDocName = "";
                cfi.BreadcrumbDocUrl = "";
                cfi.BreadcrumbFolderName = "";
                cfi.BreadcrumbFolderUrl = "";
                cfi.ClientUrl = "";
                cfi.CloseButtonClosesWindow = false;
                cfi.CloseUrl = "";
                cfi.DisableBrowserCachingOfUserContent = true;
                cfi.DisablePrint = true;
                cfi.DisableTranslation = true;
                cfi.DownloadUrl = "";
                cfi.FileUrl = "";
                cfi.FileSharingUrl = "";
                cfi.HostAuthenticationId = "s-1-5-21-3430578067-4192788304-1690859819-21774";
                cfi.HostEditUrl = "";
                cfi.HostEmbeddedEditUrl = "";
                cfi.HostEmbeddedViewUrl = "";
                cfi.HostName = @"SharePoint";
                cfi.HostNotes = @"HostBIEnabled";
                cfi.HostRestUrl = "";
                cfi.HostViewUrl = "";
                cfi.IrmPolicyDescription = "";
                cfi.IrmPolicyTitle = "";
                cfi.OwnerId = @"4257508bfe174aa28b461536d8b6b648";
                cfi.PresenceProvider = "AD";
                cfi.PresenceUserId = @"S-1-5-21-3430578067-4192788304-1690859819-21774";
                cfi.PrivacyUrl = "";
                cfi.ProtectInClient = false;
                cfi.ReadOnly = false;
                cfi.RestrictedWebViewOnly = false;
                cfi.SHA256 = "";
                cfi.SignoutUrl = "";
                cfi.Size = fi.Length;
                cfi.SupportsCoauth = false;
                cfi.SupportsCobalt = false;
                cfi.SupportsFolders = false;
                cfi.SupportsLocks = true;
                cfi.SupportsScenarioLinks = false;
                cfi.SupportsSecureStore = false;
                cfi.SupportsUpdate = true;
                cfi.TenantId = @"33b62539-8c5e-423c-aa3e-cc2a9fd796f2";
                cfi.TermsOfUseUrl = "";
                cfi.TimeZone = @"+0300#0000-11-00-01T02:00:00:0000#+0000#0000-03-00-02T02:00:00:0000#-0060";
                cfi.UserCanAttend = false;
                cfi.UserCanNotWriteRelative = false;
                cfi.UserCanPresent = false;
                cfi.UserCanWrite = true;
                cfi.UserFriendlyName = "";
                cfi.UserId = "";
                cfi.Version = @"%22%7B59CCD75F%2D0687%2D4F86%2DBBCF%2D059126640640%7D%2C1%22";
                cfi.WebEditingDisabled = false;

                // encode json
                var memoryStream = new MemoryStream();
                var json = new DataContractJsonSerializer(typeof(CheckFileInfo));
                json.WriteObject(memoryStream, cfi);
                memoryStream.Flush();
                memoryStream.Position = 0;
                StreamReader streamReader = new StreamReader(memoryStream);
                var jsonResponse = Encoding.UTF8.GetBytes(streamReader.ReadToEnd());

                context.Response.ContentType = @"application/json";
                context.Response.ContentLength64 = jsonResponse.Length;
                context.Response.OutputStream.Write(jsonResponse, 0, jsonResponse.Length);
                context.Response.Close();
            }
            else
            {
                byte[] buffer = Encoding.UTF8.GetBytes("");
                context.Response.ContentLength64 = buffer.Length;
                context.Response.ContentType = @"application/json";
                context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                context.Response.OutputStream.Close();
            }

            Listener.BeginGetContext(ProcessRequest, Listener);
        }
    }

    [DataContract]
    public class CheckFileInfo
    {
        [DataMember]
        public bool AllowExternalMarketplace { get; set; }
        [DataMember]
        public string BaseFileName { get; set; }
        [DataMember]
        public string BreadcrumbBrandName { get; set; }
        [DataMember]
        public string BreadcrumbBrandUrl { get; set; }
        [DataMember]
        public string BreadcrumbDocName { get; set; }
        [DataMember]
        public string BreadcrumbDocUrl { get; set; }
        [DataMember]
        public string BreadcrumbFolderName { get; set; }
        [DataMember]
        public string BreadcrumbFolderUrl { get; set; }
        [DataMember]
        public string ClientUrl { get; set; }
        [DataMember]
        public bool CloseButtonClosesWindow { get; set; }
        [DataMember]
        public string CloseUrl { get; set; }
        [DataMember]
        public bool DisableBrowserCachingOfUserContent { get; set; }
        [DataMember]
        public bool DisablePrint { get; set; }
        [DataMember]
        public bool DisableTranslation { get; set; }
        [DataMember]
        public string DownloadUrl { get; set; }
        [DataMember]
        public string FileSharingUrl { get; set; }
        [DataMember]
        public string FileUrl { get; set; }
        [DataMember]
        public string HostAuthenticationId { get; set; }
        [DataMember]
        public string HostEditUrl { get; set; }
        [DataMember]
        public string HostEmbeddedEditUrl { get; set; }
        [DataMember]
        public string HostEmbeddedViewUrl { get; set; }
        [DataMember]
        public string HostName { get; set; }
        [DataMember]
        public string HostNotes { get; set; }
        [DataMember]
        public string HostRestUrl { get; set; }
        [DataMember]
        public string HostViewUrl { get; set; }
        [DataMember]
        public string IrmPolicyDescription { get; set; }
        [DataMember]
        public string IrmPolicyTitle { get; set; }
        [DataMember]
        public string OwnerId { get; set; }
        [DataMember]
        public string PresenceProvider { get; set; }
        [DataMember]
        public string PresenceUserId { get; set; }
        [DataMember]
        public string PrivacyUrl { get; set; }
        [DataMember]
        public bool ProtectInClient { get; set; }
        [DataMember]
        public bool ReadOnly { get; set; }
        [DataMember]
        public bool RestrictedWebViewOnly { get; set; }
        [DataMember]
        public string SHA256 { get; set; }
        [DataMember]
        public string SignoutUrl { get; set; }
        [DataMember]
        public long Size { get; set; }
        [DataMember]
        public bool SupportsCoauth { get; set; }
        [DataMember]
        public bool SupportsCobalt { get; set; }
        [DataMember]
        public bool SupportsFolders { get; set; }
        [DataMember]
        public bool SupportsLocks { get; set; }
        [DataMember]
        public bool SupportsScenarioLinks { get; set; }
        [DataMember]
        public bool SupportsSecureStore { get; set; }
        [DataMember]
        public bool SupportsUpdate { get; set; }
        [DataMember]
        public string TenantId { get; set; }
        [DataMember]
        public string TermsOfUseUrl { get; set; }
        [DataMember]
        public string TimeZone { get; set; }
        [DataMember]
        public bool UserCanAttend { get; set; }
        [DataMember]
        public bool UserCanNotWriteRelative { get; set; }
        [DataMember]
        public bool UserCanPresent { get; set; }
        [DataMember]
        public bool UserCanWrite { get; set; }
        [DataMember]
        public string UserFriendlyName { get; set; }
        [DataMember]
        public string UserId { get; set; }
        [DataMember]
        public string Version { get; set; }
        [DataMember]
        public bool WebEditingDisabled { get; set; }
    }
}

Update for June 13th 2014:

Using Cobalt API it's possible to edit word documents, though my implementation is far from perfect. Let's get with the basics. FSSHTTP requires Shredded Storage, SharePoint implemented in the database, but after looking around Cobalt Assembly I identified the way to create a shredded file on the file system... Here are the fragments of the relevant code, this first part takes word doc from c:\tmp\test.docx and converts to shredded blobs in c:\tmp\filestore and c:\tmp\wacupdate

            DisposalEscrow disposal = new DisposalEscrow("temp1");

            CobaltFilePartitionConfig content = new CobaltFilePartitionConfig();
            content.IsNewFile = false;
            content.HostBlobStore = new FileSystemHostBlobStore("C:\\tmp\\filestore\\", "filestore", new FileSystemHostBlobStore.Config(), disposal, true, false);
            content.cellSchemaIsGenericFda = true;
            content.CellStorageConfig = new CellStorageConfig();
            content.Schema = CobaltFilePartition.Schema.ShreddedCobalt;
            content.PartitionId = FilePartitionId.Content;

            CobaltFilePartitionConfig wacupdate = new CobaltFilePartitionConfig();
            wacupdate.IsNewFile = false;
            wacupdate.HostBlobStore = new FileSystemHostBlobStore("C:\\tmp\\wacstore\\", "wacstore", new FileSystemHostBlobStore.Config(), disposal, true, false);
            wacupdate.cellSchemaIsGenericFda = false;
            wacupdate.CellStorageConfig = new CellStorageConfig();
            wacupdate.Schema = CobaltFilePartition.Schema.ShreddedCobalt;
            wacupdate.PartitionId = FilePartitionId.WordWacUpdate;

            Dictionary<FilePartitionId, CobaltFilePartitionConfig> pd = new Dictionary<FilePartitionId, CobaltFilePartitionConfig>();
            pd.Add(FilePartitionId.Content, content);
            pd.Add(FilePartitionId.WordWacUpdate, wacupdate);

    // custom locking store is my implementation of hostlockingstore
            CobaltFile cobaltFile = new CobaltFile(disposal, pd, new CustomHostLockingStore(), null);

            var src = FileAtom.FromExisting("C:\\tmp\\Test.docx", disposal);
            Cobalt.Metrics o1;
            cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).SetStream(RootId.Default.Value, src, out o1);
            cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).GetStream(RootId.Default.Value).Flush();

            cobaltFile.CommitChanges();

Now that you have the cobaltFile you can edit my original wopi code with this:

        }
        else if (context.Request.HttpMethod.Equals(@"POST") && context.Request.Headers["X-WOPI-Override"].Equals("COBALT"))
        {
            Console.WriteLine(@"Got a cobalt request for the file");
            var ms = new MemoryStream();
            context.Request.InputStream.CopyTo(ms);
            AtomFromByteArray atomRequest = new AtomFromByteArray(ms.ToArray());
            RequestBatch requestBatch = new RequestBatch();

            Object ctx;
            ProtocolVersion protocolVersion;

            requestBatch.DeserializeInputFromProtocol(atomRequest, out ctx, out protocolVersion);

            cobaltFile.CobaltEndpoint.ExecuteRequestBatch(requestBatch);
            cobaltFile.CommitChanges();


            var response = requestBatch.SerializeOutputToProtocol(protocolVersion);

            context.Response.Headers.Add("X-WOPI-MachineName", "test");
            context.Response.Headers.Add("X-WOPI-CorellationID", context.Request.Headers["X-WOPI-CorrelationID"]);
            context.Response.Headers.Add("request-id", context.Request.Headers["X-WOPI-CorrelationID"]);
            context.Response.ContentType = @"application/octet-stream";
            context.Response.ContentLength64 = response.Length;
            response.CopyTo(context.Response.OutputStream);
            context.Response.Close();

        }

and customlockingstore coming soon. Converting from shredded back to the word document I haven't done yet, but I think I know how

the final update - the complete solution is here:

https://github.com/thebitllc/WopiBasicEditor

Frontwards answered 12/6, 2013 at 12:42 Comment(16)
I have Office Web Apps setup on a virtual box on Windows server 2012 and can hit localhost/hosting/discovery . I'm wondering how to test this nowTeter
This guide is what I need I believe blogs.msdn.com/b/officedevdocs/archive/2013/03/20/…Teter
Thank you so much. If you can put an answer here #16938490 I will award the bounty to you. The same answer suits my needsTeter
We have a slight problem if you can help. Everything seems to work but cant get the file to load. The error we have I have added to my question above. In the browser it has Sorry we ran into a problem. Please Try AgainTeter
ignore error. it was to do with servers. i didnt know you needed one machine with AD DNS and another with OWA and then connected to domain etc...Teter
Lots working. How would one implement Cobalt or FSSHTTP/FSSHTTPB protocol?Teter
I see that MS-WOPI protocol is covered by Microsoft patents US 2013-0080507-A1 and US 2013-0080785-A1. I wonder if these could be a problem for those who implement WOPI server. microsoft.com/openspecifications/en/us/programs/patent-map-tool/…Gibberish
I believe WOPI only requires a paid license if you're allowing document editingVanhook
by the way, based on search on internet, I didn't see that we need to implement for "Cobalt or FSSHTTP/FSSHTTPB protocol". Could someone clarify?Nonary
@Nonary if you want editing of word documents you need to implement FSSHTTP protocolTeter
If you're using the Cobalt types from the SharePoint assemblies, means you're using SharePoint - and you're licensed for it. Might want to decouple from those assemblies and use them for inspiration...Assailant
@Cicorias this assembly is present in Office Web Apps Server.Frontwards
Hi All, I have the same issue as @Teter once faced, I am trying to use just Office Web Apps Server as a client. I have configured client and server on Windows server 2012 OS wac_domain/hosting/discovery url is working, and I have also implemented the host. Everything is working but file is not loading on the browser throwing error like 'sorry there was a problem we can't open this document'.Alkylation
I am hearing about one dll everywhere talking about OWA eidting docx. But where to get this Microsoft.cobaltcore.dll?Thanet
Hi everyone, I just want to ask that can I implement WOPI server in Nodejs?Waldman
Has anyone been able to implement OneNote editing using Cobalt?Confederation

© 2022 - 2024 — McMap. All rights reserved.