PHPackages                             champs-libres/wopi-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. champs-libres/wopi-bundle

ActiveSymfony-bundle[API Development](/categories/api)

champs-libres/wopi-bundle
=========================

A bundle providing routes and glue code between Symfony and a WOPI connector.

v1.0.0(5mo ago)88.1k↓40.9%2[4 issues](https://github.com/Champs-Libres/wopi-bundle/issues)1MITPHPPHP &gt;= 8.3CI passing

Since Jun 20Pushed 5mo ago2 watchersCompare

[ Source](https://github.com/Champs-Libres/wopi-bundle)[ Packagist](https://packagist.org/packages/champs-libres/wopi-bundle)[ Docs](http://github.com/champs-libres/wopi-bundle)[ RSS](/packages/champs-libres-wopi-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (17)Versions (7)Used By (1)

[![Latest Stable Version](https://camo.githubusercontent.com/f0f737ca043f16840962ece8cea4fe555f09923d68372d59b1dc66fff094dce4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6368616d70732d6c69627265732f776f70692d62756e646c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/champs-libres/wopi-bundle)[![GitHub stars](https://camo.githubusercontent.com/b8f2bbd15adac0470b8902ac3a4e144514642752ee7517842ecfb5c68da4d2fc/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6368616d70732d6c69627265732f776f70692d62756e646c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/champs-libres/wopi-bundle)[![Total Downloads](https://camo.githubusercontent.com/140e1587b3d39ceb07aecb2d9423b5117946f587b04baac7da2c507210e356fd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6368616d70732d6c69627265732f776f70692d62756e646c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/champs-libres/wopi-bundle)[![GitHub Workflow Status](https://camo.githubusercontent.com/085273fdf7ead019f93b619f0efc3e11e6e8c4ab47cafb701298113762024f2e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f6368616d70732d6c69627265732f776f70692d62756e646c652f556e697425323074657374733f7374796c653d666c61742d737175617265)](https://github.com/champs-libres/wopi-bundle/actions)[![Scrutinizer code quality](https://camo.githubusercontent.com/22f70967e0c8807f2e730542ad425da5a26dcc0506ce84b4f457c0c665efb862/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f7175616c6974792f672f6368616d70732d6c69627265732f776f70692d62756e646c652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/champs-libres/wopi-bundle/?branch=master)[![Type Coverage](https://camo.githubusercontent.com/d6237dd9fcce8e698710881f125bf923e0ce1d3a4a07c040033d6ca2ab5ac8ba/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f64796e616d69632f6a736f6e3f7374796c653d666c61742d73717561726526636f6c6f723d636f6c6f72266c6162656c3d54797065253230636f7665726167652671756572793d6d6573736167652675726c3d687474707325334125324625324673686570686572642e6465762532466769746875622532466368616d70732d6c6962726573253246776f70692d62756e646c65253246636f766572616765)](https://shepherd.dev/github/champs-libres/wopi-bundle)[![Code Coverage](https://camo.githubusercontent.com/270b315217eef8bc537e2afb0584b51bdd8f8c5e19f65259086ba4d8c76a0b54/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f636f7665726167652f672f6368616d70732d6c69627265732f776f70692d62756e646c652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/champs-libres/wopi-bundle/?branch=master)[![License](https://camo.githubusercontent.com/d38c92d0cf17b32984948fe7316a218a8eb1c379301fa938036c5580dbf99149/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6368616d70732d6c69627265732f776f70692d62756e646c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/champs-libres/wopi-bundle)

WOPI Bundle
===========

[](#wopi-bundle)

A Symfony bundle to facilitate the implementation of the WOPI endpoints and protocol.

Description
-----------

[](#description)

The **W**eb Application **O**pen **P**latform **I**nterface (WOPI) protocol let you integrate Office for the web with your application, but also other software like [Collabora Online](https://www.collaboraoffice.com/collabora-online/)

This bundle targets the integration with Collabora Online, for now.

In the future, this bundle may achieve [a validation](https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/build-test-ship/testing) for an usage with Office For The Web.

### Integration of Collabora Online

[](#integration-of-collabora-online)

- [Collabora Online](https://sdk.collaboraonline.com)

### Overview for WOPI protocol

[](#overview-for-wopi-protocol)

- [wopi](https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/)

### Office for the web platforms:

[](#office-for-the-web-platforms)

- [Collabora Office](https://www.collaboraoffice.com/)
- [Office 365](https://www.office.com/)

Installation
------------

[](#installation)

`composer require champs-libres/wopi-bundle`

Usage
-----

[](#usage)

This bundle provides the basic implementation of the protocol into Symfony. But there are many ways to:

- store documents in an application;
- secure the protocol
- and manage permission, according to your own business logic.

Therefore, this bundle does not provide a specific implementation of the WOPI protocol described through [a basic interface](https://github.com/Champs-Libres/wopi-lib/blob/master/src/WopiInterface.php) from the [champs-libres/wopi-lib](https://github.com/champs-libres/wopi-lib) bundle.

So, this bundle provides:

- The [routes](https://github.com/Champs-Libres/wopi-bundle/blob/master/src/Resources/config/routes/routes.php) that the WOPI protocol needs, which starts with `/wopi` path (required by the WOPI protocol);
- A [controller](https://github.com/Champs-Libres/wopi-bundle/blob/master/src/Controller/Files.php) to for the WOPI routes;
- And an implementation for the Wopi logic, which will re-use some of **your** logic to manager permission, document, etc.

Some vocabulary:

- Wopi host: the app which implements this bundle;
- Wopi client: Collabora Online (or Office 365), which will use the endpoint provided by your app (the host)
- Editor: Collabora Online (or office 365). A synonym for Wopi client.

These are steps to integrate the wopi bundle in your application:

### Start an editor / your wopi client for development

[](#start-an-editor--your-wopi-client-for-development)

You will find a free collabora online with the CODE project: [CODE](https://www.collaboraoffice.com/code/).

⚠️ the editor must have access to your app, **with the same domain name as the browser will open your app**.

If you use docker and docker-compose, you can achieve this by manipulating your `/etc/hosts` file:

```
# docker-compose.yaml

services:
    app:
        # your php / symfony application
        # we assume that your app listen **inside the container** on the port 8001 (no port mapping required between inside and
        # outside of the container)
        # ...
    collabora:
        image: collabora/code:latest
        environment:
            - SLEEPFORDEBUGGER=0
            - DONT_GEN_SSL_CERT="True"
            - extra_params=--o:ssl.enable=false --o:ssl.termination=false
            - username=admin
            - password=admin
            - dictionaries=en_US
            - aliasgroup1=http://nginx:8001
        ports:
            - "127.0.0.1:9980:9980"
        cap_add:
            - MKNOD
        links:
            - app
```

```
# /etc/hosts

127.0.0.1 app collabora

```

With this config, you should be able to reach collabora using , and your app through . You must use the latter to access your app during debugging collabora features.

### Configure this bundle

[](#configure-this-bundle)

```
# app/config/package/wopi.yaml

wopi:
    # this is the path to your server.
    # note: the wopi client (Collabora) must be able to your app **using the same domains as your browser**
    server: http://collabora:9980
```

### Create your document entity

[](#create-your-document-entity)

Each document edited should be an entity which implements [`Document`](https://github.com/Champs-Libres/wopi-lib/blob/master/src/Contract/Entity/Document.php).

### Create your document manager

[](#create-your-document-manager)

Your manager will implements [`DocumentManagerInterface`](https://github.com/Champs-Libres/wopi-lib/blob/master/src/Contract/Service/DocumentManagerInterface.php).

This DocumentManager will handle the document logic into your application. It provides methods for writing the document, and extract some information from it.

You can read [an implementation here](https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php).

### Create your logic for access token

[](#create-your-logic-for-access-token)

**`access_token`** are created by your app, when it will open the editor page (spoiler: the editor page will be an iframe). The wopi host (your application) will receive this access token on every request made by the client. Each token should have a duration of 10 hours.

**You can choose your own logic**. But JWT can ease your life.

#### Some working configuration using LexikJWT

[](#some-working-configuration-using-lexikjwt)

An easy way to authenticate your request is to use [JWT (Json Web Token)](https://jwt.io/). This can be achieved easily with [LexikJWTAuthenticationBundle](https://symfony.com/bundles/LexikJWTAuthenticationBundle/current/index.html).

Create a firewall and configure access control for url starting by `/wopi`:

```
# config/package/security.yaml
security:
    firewalls:
        wopi:
            pattern: ^/wopi
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
    access_control:
        # ...
        - { path: ^/wopi, roles: IS_AUTHENTICATED_FULLY }
        # ...
```

Configure lexik:

```
# config/package/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    # required for wopi - recommended duration for token ttl
    token_ttl: 36000

    # required for wopi: the token is in query, with `?access_token=`
    token_extractors:
        query_parameter:
            enabled: true
            name: access_token
```

See a working implementation:

### Provide information about your user

[](#provide-information-about-your-user)

Implements [`UserManagerInterface`](./src/Contracts/UserManagerInterface.php) to provide information about your users.

This information should be extracted through access token.

[Some working implementation](https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php)

### Provide information about the permissions / authorization

[](#provide-information-about-the-permissions--authorization)

Implements [`AuthorizationManagerInterface`](./src/Contracts/AuthorizationManagerInterface.php) to provide information about the permissions on the given Document.

[Some working implementation](https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillWopiBundle/src/Service/Wopi/AuthorizationManager.php)

### Bind all the services

[](#bind-all-the-services)

This bundle will require the implementation to be name according to the interface.

Some example:

```
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface;
use ChampsLibres\WopiBundle\Contracts\UserManagerInterface;
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
use Chill\WopiBundle\Service\Wopi\AuthorizationManager;
use Chill\WopiBundle\Service\Wopi\ChillDocumentManager;
use Chill\WopiBundle\Service\Wopi\UserManager;

return static function (ContainerConfigurator $container) {
    $services = $container
        ->services();

    $services
        ->defaults()
        ->autowire()
        ->autoconfigure();

    $services
        ->set(ChillDocumentManager::class);

    $services
        ->alias(DocumentManagerInterface::class, ChillDocumentManager::class);

    $services
        ->set(AuthorizationManager::class);

    $services->alias(AuthorizationManagerInterface::class, AuthorizationManager::class);

    $services
        ->set(UserManager::class);

    $services->alias(UserManagerInterface::class, UserManager::class);
};
```

### Create an editor page

[](#create-an-editor-page)

The editor page will be the page which will load the editor, through an iframe.

Here is a controller:

```
