Get an Office Editor into your OutSystems apps with WOPI
If you’re building document-centric applications, at some point you’re likely to reach a situation where you don’t just want to view documents, but also create or edit them. Directly in the browser and without media breaks. The Microsoft WOPI protocol and a WOPI compatible Office Suite make this possible.
One use case, for example, is the automatic generation of a contract (e.g., via Adobe Document Generation) as a draft with final adjustments.
This article accompanies a Reference Implementation of the WOPI protocol in OutSystems which i published on OutSystems Forge. (See below).
Low Code High-Performance Software Development | OutSystems
OutSystems creates high-performance low-code and cloud native development platforms for visionary organizations at a…
WOPI — Web Application Open Platform Interface
The Web Application Open Platform Interface is a protocol specification by Microsoft. It defines a set of operations to integrate Office for the Web with your application. In other words — WOPI lets Office for the Web read and edit documents that are served by your service.
Office for the Web refers to browser-based versions of Microsoft Office Suite (Word, Excel, PowerPoint, OneNote). Office for the Web is either available in the (Microsoft) Cloud as part of a Microsoft 365 or Office 365 subscription or self-hosted using Microsoft Office Online Server.
That said — WOPI is not limited to Office for the Web but has also been adopted by alternative Office vendors like
- LibreOffice Online by The Document Foundation. A complete open-source Office Suite.
- Collabora Online — A LibreOffice based enterprise-ready Office Suite.
- ONLYOFFICE — A complete Office Suite with full support for the Microsoft Office document format (OOXML).
Office Suites reading and editing files via the WOPI protocol are called WOPI Client, while application services serving these documents are referred to as WOPI Host.
Integrating a WOPI client is also useful if you need a viewer for displaying documents in the browser. All WOPI clients support a simplified reading mode for documents — not only for Office documents but also for the common PDF format.
For this reference implementation im using ONLYOFFICE as WOPI client. ONLYOFFICE meets the WOPI specification, although it doesn’t provide the same feature set as Office for the Web. I used it merely because it is easier to setup than Microsoft alternatives:
- Office for the Web Cloud service requires you to become a member of the Office 365 Cloud Storage Partner Program. Integrating with Office for the Web Cloud service is only applicable if you build a SaaS product as ISV.
- Office Online Server (Self-Hosted) requires you to have access to the software download and needs some preparations before you can use it.
Anyhow. If you know how to integrate with ONLYOFFICE you are good to go with the other options.
ONLYOFFICE by Ascensio System SIA offers a powerful office suite that comprises online editors for text documents, spreadsheets, and presentations highly compatible with Microsoft Office and OpenDocument file formats. ONLYOFFICE provides users with multiple editing tools and collaborative features ensuring greater team workflow, and seamless work with complex formatting and objects within your web solution.
The big picture
For ONLYOFFICE to render documents from our application, we need the following building blocks:
- WOPI Host — The host is an exposed REST API with operations for serving and saving files. In OutSystems this is a set of exposed REST endpoints.
- WOPI Client — A running instance of ONLYOFFICE. We are going to use the official ONLYOFFICE docker image for that.
- WOPI Host Page — A web page which does the connection between the host and the client.
Setup development environment
Before we step through the OutSystems reference implementation we must make sure that you have a running development/tryout environment. This is what you need.
Run ONLYOFFICE Docker Desktop
To run an ONLYOFFICE docker container first create some local directories e.g.
These directories will be used as mounted volumes in our container to persist data. Then run the following command.
docker run -it -d -p 80:80 --restart=always \
-v C:\dev\onlyoffice\logs:/var/log/onlyoffice \
-v C:\dev\onlyoffice\data:/var/www/onlyoffice\data \
-v C:\dev\onlyoffice\lib:/var/lib/onlyoffice \
-v C:\dev\onlyoffice\db:/var/lib/postgresql onlyoffice/documentserver
This will start the container running on port 80. On my machine it takes about 1minute until onlyoffice is fully started. Try to browse to the start page on http://localhost. You should see the following screen.
When drafting this article, the most current version of ONLYOFFICE was 7.1.
More information about running ONLYOFFICE in Docker can be found in the ONLYOFFICE Helpcenter.
Enable ONLYOFFICE WOPI Support
By default, WOPI support is disabled, so we need to activate it first and set some additional configurations. Start Visual Studio Code with Docker extensions installed and open the file at
The local.json file is an override to the default.json configuration file in the same directory. default.json contains all available configuration options for ONLYOFFICE.
Within services add a new object ipfilter
This setting tells OPENOFFICE to accept requests from any address. Dependent on the version you are using this may is already set.
Within services -> CoAuthoring add a new object autoAssembly
Configure OPENOFFICE to post changes in a document immediately back to the WOPI Host.
Without this setting there is a risk that you produce a version mismatch between the local edited file and the WOPI host stored file.
And directly in the root of the local.json file add a new wopi object
This enables the WOPI integration 😒
More on enabling WOPI support can be found in the ONLYOFFICE documentation
Starting from version 6.4, ONLYOFFICE Docs offers support for the Web Application Open Platform Interface Protocol…
Restart ONLYOFFICE by opening a terminal session and execute the following command
sudo supervisorctl restart all
OutSystems Reference Implementation
You can download the reference implementation application from the Forge. Available here
The reference implementation consists of several modules and should give you a starting point for your own solution to adapt and extend.
- DocumentStore_CS — A simple implementation of a document storage provider using OutSystems entities to store documents.
In a real implementation you may want to offload document storage to an alternative like AWS S3.
- DocumentStoreUtil_EXT — An extension module to parse a filename. There is only one action which extracts the extension of a document and determines the MIME type based on the extension.
- WOPI_API — Is the implementation of the WOPI Host. Contains several REST endpoints to Get, Update, Lock and Unlock a document.
- WOPIUtil_EXT — An extension with an action to validate the “Proof” sent by Office with every request.
The proof allows the WOPI Host (our OutSystems REST API) to validate if the request was sent by the WOPI Client.
- WOPI_LIB — This library contains a single UI widget to use on the WOPI Host Page.
- WOPIDemo — A small demo application bringing all pieces together.
Configure the application
Before running the application there are some configuration settings to set. The WOPI_API module exposes some site properties for that.
- WOPI_ClientBaseUrl — Is the full url to your ONLYOFFICE Wopi Endpoints. When running locally in Docker Desktop this must be set to http://localhost/hosting/wopi.
- WOPI_HostBaseUrl — Is the full url to your environment's WOPI REST endpoints. Should be https://<DNS of your OutSystems environment>/WOPI_API/rest
Next open a browser and go to http://localhost/hosting/discovery
The discovery xml document shows you the capabilities of ONLYOFFICE. It tells you what you can do with the various file types regarding view or edit.
Scroll to the proof-key element of the document and copy
- value to WOPI_Key
- modulus to WOPI_Modulus
- exponent to WOPI_Exponent
- oldvalue to WOPI_KeyOld
- oldmodulus to WOPI_ModulusOld
- oldexponent to WOPI_ExponentOld
These values are used to validate every request from ONLYOFFICE to the REST endpoints. More on proof key validation here
Verify that requests originate from Office for the web by using proof keys
When processing WOPI requests from Office for the web, you might want to verify that these requests are coming from…
Here we are statically copying the key elements for validation to our environment. When using Office for the Web and/or if you want to constantly change your keys — what you should do — then you should query the discovery document on a schedule, extract the proof-key element and cache it in an entity.
Start the demo application and upload an office document using the Upload Document button. Then click on the name of the document to open it in ONLYOFFICE.
Edit the document you uploaded. Notice the Save icon in the top menu. Whenever you stop writing ONLYOFFICE performs a save and posts the file to the WOPI PutFile endpoint.
When you click on the document name you are redirected to another screen which is the WOPI Host Page. The Host Page includes a full screen IFRAME hosting ONLYOFFICE. The Host Page makes a Form POST operation to this IFRAME with a WOPI Source URL and an access code.
The WOPI Source URL tells ONLYOFFICE to get the document from that URL — which is our REST endpoint in the WOPI_API module. The access code identifies the user and is directly forwarded to our API.
So, it is ONLYOFFICE which connects to our OutSystems environment and the user's browser is the connection between ONLYOFFICE and OutSystems. OutSystems itself never directly connects with ONLYOFFICE, which also explains why you can run and use ONLYOFFICE locally.
Let's look at some important implementations.
WOPI REST Endpoints
Open the WOPI_API module, go to Logic tab and expand the exposed WOPI REST service.
- CheckFileInfo — Whenever ONLYOFFICE needs to load a document, this endpoint is queried first. The endpoint returns basic configuration data and information about the document itself.
The CheckFileInfo operation returns information about a file, a user's permissions on that file, and general…
- GetFile — This endpoint returns the actual binary data of a document.
- PutFile — Whenever a document is saved this operation receives the binary data.
The PutFile operation updates a file's binary contents. The PutFile operation updates a file's binary contents. WOPI…
- FileOperation — Is primarily used for Locking and Unlocking a document.
The Lock operation locks a file for editing by the WOPI client application instance that requested the lock. The Lock…
The GetLock operation retrieves a lock on a file. The GetLock operation retrieves a lock on a file. This operation…
The RefreshLock operation refreshes the lock on a file. The RefreshLock operation refreshes the lock on a file by…
The Unlock operation releases the lock on a file. The Unlock operation releases the lock on a file. WOPI clients…
The reference implementation features the minimum implementation. If you look at the WOPI reference, you will notice that Office for the Web has lots of additional functionality provided by the WOPI protocol.
The OnAuthentication flow performs the following
- Retrieves the Proof headers from the request
- Validates the Proof — provided by the WOPIUtil_EXT module
- Parses and validates the access token of the request
- Performs a login of the user given in the access token. (From System).
The reference implementation contains a simple document storage using entities — DocumentStore_CS. Personally, i recommend storing documents in an Object Storage. Examples are
And some storage vendors have their own (more or less) compatible S3 option.
Besides binary data, the document store also saves additional information for a file like extension of a document and the lock state.
WOPI Host Page
Now for the most interesting part. The WOPI Host Page. Open the WOPIDemo Reactive Web, the WOPI_LIB and the WOPI_API module in Service Studio.
In the WOPIDemo Demo Screen, when you click the name link you get redirected to another screen HostPage. That HostPage screen takes the DocumentId as input parameter. We skip the action parameter for the moment.
The host page has a data action GetResourceAccess which executes a server action from the WOPI_API module WOPI_GetDefaultHostPageConfig.
WOPI_GetDefaultHostPageConfig combines two other server actions into a single call
- WOPI_GetDocumentAccessCode — This one creates an access code which is needed by our REST endpoints to identify the user. The access code is handed over to ONLYOFFICE. But! ONLYOFFICE does nothing with the access code but includes it into every request to our API.
Another reminder here: The implementation contains an extremely basic access token creation and validation. In a real scenario you should create a signed JSON Web Token and validate the signature on every request.
- WOPI_GetDocumentActionUrl — This constructs an URL for letting ONLYOFFICE know what application (Word, Excel, Powerpoint) to open and in what mode (view or edit).
To construct the action URL, the server action first retrieves the document details from the document store. Then it queries the static entity DocType in WOPI_API to determine the default application and mode for a given document extension.
I only included some of the supported file type extension to the static entity. Check the discovery template for more supported file types and add them.
Along with the access code and the action URL there is also a time-to-live returned to the HostPage. This is set to 10 hours in milliseconds as recommended by the WOPI specification.
Go to the widget tree of the HostPage. Note that it includes the ONLYOFFICE widget from WOPI_LIB. It is necessary to wrap it in an IF with the condition set to GetResourceAccess.IsDataFetched. This prevents rendering of the widget before the necessary data is retrieved.
The widget itself renders a FORM and an IFRAME and submits access code, ttl and action URL to the form in the OnReady event handler.
Thank you for reading. I hope you liked it and that i have explained the important parts well. Let me know if not 😊
Using ONLYOFFICE i experienced some issues. There is a considerable delay between a save operation and ONLYOFFICE recognizing an updated version. This sometimes led to a version mismatch message. In addition, there is a low inactivity expiration setting. That “expire messages” are annoying, but i couldn't find a setting to increase it. It asked a question about that in their developer forum.
But anyhow — It is a viable alternative if you do not have access to an Office Online server.
If you have difficulties in getting up and running, please use the OutSystems Forum to get help. Suggestions on how to improve this article are very welcome. Send me a message via my OutSystems Profile or respond directly here on medium. Happy low coding!